diff --git a/dev/util_gen_stub_file.py b/dev/util_gen_stub_file.py index 2c6519994..ac95f40a9 100644 --- a/dev/util_gen_stub_file.py +++ b/dev/util_gen_stub_file.py @@ -36,6 +36,9 @@ "__next__", } skip = { + "__firstlineno__", + "__static_attributes__", + "__replace__", "__builtins__", "__cached__", "__getstate__", @@ -99,6 +102,7 @@ def splay_signature(sig: str) -> List[str]: sig = sig.replace('dict[', 'Dict[') sig = sig.replace('tuple[', 'Tuple[') sig = sig.replace('set[', 'Set[') + sig = sig.replace('pathlib._local.Path', 'pathlib.Path') assert sig.startswith('def') out = [] diff --git a/doc/python_api_reference_vDev.md b/doc/python_api_reference_vDev.md index 9b1043290..542813f5d 100644 --- a/doc/python_api_reference_vDev.md +++ b/doc/python_api_reference_vDev.md @@ -195,6 +195,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi - [`stim.FlipSimulator.clear`](#stim.FlipSimulator.clear) - [`stim.FlipSimulator.copy`](#stim.FlipSimulator.copy) - [`stim.FlipSimulator.do`](#stim.FlipSimulator.do) + - [`stim.FlipSimulator.generate_bernoulli_samples`](#stim.FlipSimulator.generate_bernoulli_samples) - [`stim.FlipSimulator.get_detector_flips`](#stim.FlipSimulator.get_detector_flips) - [`stim.FlipSimulator.get_measurement_flips`](#stim.FlipSimulator.get_measurement_flips) - [`stim.FlipSimulator.get_observable_flips`](#stim.FlipSimulator.get_observable_flips) @@ -204,6 +205,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi - [`stim.FlipSimulator.num_qubits`](#stim.FlipSimulator.num_qubits) - [`stim.FlipSimulator.peek_pauli_flips`](#stim.FlipSimulator.peek_pauli_flips) - [`stim.FlipSimulator.set_pauli_flip`](#stim.FlipSimulator.set_pauli_flip) + - [`stim.FlipSimulator.to_numpy`](#stim.FlipSimulator.to_numpy) - [`stim.FlippedMeasurement`](#stim.FlippedMeasurement) - [`stim.FlippedMeasurement.__init__`](#stim.FlippedMeasurement.__init__) - [`stim.FlippedMeasurement.observable`](#stim.FlippedMeasurement.observable) @@ -4315,6 +4317,17 @@ def __init__( repeat_count: The number of times to repeat the block. body: The body of the block, as a circuit. tag: Defaults to empty. A custom string attached to the REPEAT instruction. + + Examples: + >>> import stim + >>> c = stim.Circuit() + >>> c.append(stim.CircuitRepeatBlock(100, stim.Circuit("M 0"))) + >>> c + stim.Circuit(''' + REPEAT 100 { + M 0 + } + ''') """ ``` @@ -4508,6 +4521,16 @@ def __init__( targets_in_range: List[stim.GateTargetWithCoords], ) -> None: """Creates a stim.CircuitTargetsInsideInstruction. + + Examples: + >>> import stim + >>> val = stim.CircuitTargetsInsideInstruction( + ... gate='X_ERROR', + ... args=[0.25], + ... target_range_start=0, + ... target_range_end=1, + ... targets_in_range=[stim.GateTargetWithCoords(0, [])], + ... ) """ ``` @@ -4521,6 +4544,19 @@ def args( self, ) -> List[float]: """Returns parens arguments of the gate / instruction that was being executed. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.args + [0.25] """ ``` @@ -4534,6 +4570,19 @@ def gate( self, ) -> Optional[str]: """Returns the name of the gate / instruction that was being executed. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.gate + 'X_ERROR' """ ``` @@ -4548,6 +4597,21 @@ def target_range_end( ) -> int: """Returns the exclusive end of the range of targets that were executing within the gate / instruction. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.target_range_start + 0 + >>> loc.instruction_targets.target_range_end + 1 """ ``` @@ -4562,6 +4626,21 @@ def target_range_start( ) -> int: """Returns the inclusive start of the range of targets that were executing within the gate / instruction. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.target_range_start + 0 + >>> loc.instruction_targets.target_range_end + 1 """ ``` @@ -4577,6 +4656,19 @@ def targets_in_range( """Returns the subset of targets of the gate/instruction that were being executed. Includes coordinate data with the targets. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.targets_in_range + [stim.GateTargetWithCoords(0, [])] """ ``` @@ -6210,6 +6302,14 @@ class DemTargetWithCoords: problem in a circuit, instead of having to constantly manually look up the coordinates of a detector index in order to understand what is happening. + + Examples: + >>> import stim + >>> t = stim.DemTargetWithCoords(stim.DemTarget("D1"), [1.5, 2.0]) + >>> t.dem_target + stim.DemTarget('D1') + >>> t.coords + [1.5, 2.0] """ ``` @@ -6220,7 +6320,6 @@ class DemTargetWithCoords: # (in class stim.DemTargetWithCoords) def __init__( self, - *, dem_target: stim.DemTarget, coords: List[float], ) -> None: @@ -7401,6 +7500,28 @@ def to_file( # (at top-level in the stim module) class ExplainedError: """Describes the location of an error mechanism from a stim circuit. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0]) + ExplainedError { + dem_error_terms: L0 + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } + } """ ``` @@ -7416,6 +7537,28 @@ def __init__( circuit_error_locations: List[stim.CircuitErrorLocation], ) -> None: """Creates a stim.ExplainedError. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0]) + ExplainedError { + dem_error_terms: L0 + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } + } """ ``` @@ -7437,6 +7580,25 @@ def circuit_error_locations( Note: if this list is empty, it may be because there was a DEM error decomposed into parts where one of the parts is impossible to make on its own from a single circuit error. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0].circuit_error_locations[0]) + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } """ ``` @@ -7804,6 +7966,76 @@ def do( """ ``` + +```python +# stim.FlipSimulator.generate_bernoulli_samples + +# (in class stim.FlipSimulator) +def generate_bernoulli_samples( + self, + num_samples: int, + *, + p: float, + bit_packed: bool = False, + out: Optional[np.ndarray] = None, +) -> np.ndarray: + """Uses the simulator's random number generator to produce biased coin flips. + + This method has best performance when specifying `bit_packed=True` and + when specifying an `out=` parameter pointing to a numpy array that has + contiguous data aligned to a 64 bit boundary. (If `out` isn't specified, + the returned numpy array will have this property.) + + Args: + num_samples: The number of samples to produce. + p: The probability of each sample being True instead of False. + bit_packed: Defaults to False (no bit packing). When True, the result + has type np.uint8 instead of np.bool_ and 8 samples are packed into + each byte as if by np.packbits(bitorder='little'). (The bit order + is relevant when producing a number of samples that isn't a multiple + of 8.) + out: Defaults to None (allocate new). A numpy array to write the samples + into. Must have the correct size and dtype. + + Returns: + A numpy array containing the samples. The shape and dtype depends on + the bit_packed argument: + + if not bit_packed: + shape = (num_samples,) + dtype = np.bool_ + elif not transpose and bit_packed: + shape = (math.ceil(num_samples / 8),) + dtype = np.uint8 + + Raises: + ValueError: + The given `out` argument had a shape or dtype inconsistent with the + requested data. + + Examples: + >>> import stim + >>> sim = stim.FlipSimulator(batch_size=256) + >>> r = sim.generate_bernoulli_samples(1001, p=0.25) + >>> r.dtype + dtype('bool') + >>> r.shape + (1001,) + + >>> r = sim.generate_bernoulli_samples(53, p=0.1, bit_packed=True) + >>> r.dtype + dtype('uint8') + >>> r.shape + (7,) + >>> r[6] & 0b1110_0000 # zero'd padding bits + np.uint8(0) + + >>> r2 = sim.generate_bernoulli_samples(53, p=0.2, bit_packed=True, out=r) + >>> r is r2 # Check request to reuse r worked. + True + """ +``` + ```python # stim.FlipSimulator.get_detector_flips @@ -8232,6 +8464,165 @@ def set_pauli_flip( """ ``` + +```python +# stim.FlipSimulator.to_numpy + +# (in class stim.FlipSimulator) +def to_numpy( + self, + *, + bit_packed: bool = False, + transpose: bool = False, + output_xs: Union[bool, np.ndarray] = False, + output_zs: Union[bool, np.ndarray] = False, + output_measure_flips: Union[bool, np.ndarray] = False, + output_detector_flips: Union[bool, np.ndarray] = False, + output_observable_flips: Union[bool, np.ndarray] = False, +) -> Optional[Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]]: + """Writes the simulator state into numpy arrays. + + Args: + bit_packed: Whether or not the result is bit packed, storing 8 bits per + byte instead of 1 bit per byte. Bit packing always applies to + the second index of the result. Bits are packed in little endian + order (as if by `np.packbits(X, axis=1, order='little')`). + transpose: Defaults to False. When set to False, the second index of the + returned array (the index affected by bit packing) is the shot index + (meaning the first index is the qubit index or measurement index or + etc). When set to True, results are transposed so that the first + index is the shot index. + output_xs: Defaults to False. When set to False, the X flip data is not + generated and the corresponding array in the result tuple is set to + None. When set to True, a new array is allocated to hold the X flip + data and this array is returned via the result tuple. When set to + a numpy array, the results are written into that array (the shape and + dtype of the array must be exactly correct). + output_zs: Defaults to False. When set to False, the Z flip data is not + generated and the corresponding array in the result tuple is set to + None. When set to True, a new array is allocated to hold the Z flip + data and this array is returned via the result tuple. When set to + a numpy array, the results are written into that array (the shape and + dtype of the array must be exactly correct). + output_measure_flips: Defaults to False. When set to False, the measure + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the measure flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + output_detector_flips: Defaults to False. When set to False, the detector + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the detector flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + output_observable_flips: Defaults to False. When set to False, the obs + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the obs flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + + Returns: + A tuple (xs, zs, ms, ds, os) of numpy arrays. The xs and zs arrays are + the pauli flip data specified using XZ encoding (00=I, 10=X, 11=Y, 01=Z). + The ms array is the measure flip data, the ds array is the detector flip + data, and the os array is the obs flip data. The arrays default to + `None` when the corresponding `output_*` argument was left False. + + The shape and dtype of the data depends on arguments given to the function. + The following specifies each array's shape and dtype for each case: + + if not transpose and not bit_packed: + xs.shape = (sim.batch_size, sim.num_qubits) + zs.shape = (sim.batch_size, sim.num_qubits) + ms.shape = (sim.batch_size, sim.num_measurements) + ds.shape = (sim.batch_size, sim.num_detectors) + os.shape = (sim.batch_size, sim.num_observables) + xs.dtype = np.bool_ + zs.dtype = np.bool_ + ms.dtype = np.bool_ + ds.dtype = np.bool_ + os.dtype = np.bool_ + elif not transpose and bit_packed: + xs.shape = (sim.batch_size, math.ceil(sim.num_qubits / 8)) + zs.shape = (sim.batch_size, math.ceil(sim.num_qubits / 8)) + ms.shape = (sim.batch_size, math.ceil(sim.num_measurements / 8)) + ds.shape = (sim.batch_size, math.ceil(sim.num_detectors / 8)) + os.shape = (sim.batch_size, math.ceil(sim.num_observables / 8)) + xs.dtype = np.uint8 + zs.dtype = np.uint8 + ms.dtype = np.uint8 + ds.dtype = np.uint8 + os.dtype = np.uint8 + elif transpose and not bit_packed: + xs.shape = (sim.num_qubits, sim.batch_size) + zs.shape = (sim.num_qubits, sim.batch_size) + ms.shape = (sim.num_measurements, sim.batch_size) + ds.shape = (sim.num_detectors, sim.batch_size) + os.shape = (sim.num_observables, sim.batch_size) + xs.dtype = np.bool_ + zs.dtype = np.bool_ + ms.dtype = np.bool_ + ds.dtype = np.bool_ + os.dtype = np.bool_ + elif transpose and bit_packed: + xs.shape = (sim.num_qubits, math.ceil(sim.batch_size / 8)) + zs.shape = (sim.num_qubits, math.ceil(sim.batch_size / 8)) + ms.shape = (sim.num_measurements, math.ceil(sim.batch_size / 8)) + ds.shape = (sim.num_detectors, math.ceil(sim.batch_size / 8)) + os.shape = (sim.num_observables, math.ceil(sim.batch_size / 8)) + xs.dtype = np.uint8 + zs.dtype = np.uint8 + ms.dtype = np.uint8 + ds.dtype = np.uint8 + os.dtype = np.uint8 + + Raises: + ValueError: + All the `output_*` arguments were False, or an `output_*` argument + had a shape or dtype inconsistent with the requested data. + + Examples: + >>> import stim + >>> import numpy as np + >>> sim = stim.FlipSimulator(batch_size=9) + >>> sim.do(stim.Circuit('M(1) 0 1 2')) + + >>> ms_buf = np.empty(shape=(9, 1), dtype=np.uint8) + >>> xs, zs, ms, ds, os = sim.to_numpy( + ... transpose=True, + ... bit_packed=True, + ... output_xs=True, + ... output_measure_flips=ms_buf, + ... ) + >>> assert ms is ms_buf + >>> xs + array([[0], + [0], + [0], + [0], + [0], + [0], + [0], + [0], + [0]], dtype=uint8) + >>> zs + >>> ms + array([[7], + [7], + [7], + [7], + [7], + [7], + [7], + [7], + [7]], dtype=uint8) + >>> ds + >>> os + """ +``` + ```python # stim.FlippedMeasurement @@ -8242,6 +8633,18 @@ class FlippedMeasurement: Gives the measurement's index in the measurement record, and also the observable of the measurement. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement + stim.FlippedMeasurement( + record_index=1, + observable=(stim.GateTargetWithCoords(stim.target_z(10), []),), + ) """ ``` @@ -8282,6 +8685,15 @@ def observable( """Returns the observable of the flipped measurement. For example, an `MX 5` measurement will have the observable X5. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement.observable + [stim.GateTargetWithCoords(stim.target_z(10), [])] """ ``` @@ -8297,6 +8709,15 @@ def record_index( """The measurement record index of the flipped measurement. For example, the fifth measurement in a circuit has a measurement record index of 4. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement.record_index + 1 """ ``` @@ -9391,7 +9812,21 @@ def __init__( """Initializes a `stim.GateTarget`. Args: - value: A target like `5` or `stim.target_rec(-1)`. + value: A value to convert into a gate target, like an integer + to interpret as a qubit target or a string to parse. + + Examples: + >>> import stim + >>> stim.GateTarget(stim.GateTarget(5)) + stim.GateTarget(5) + >>> stim.GateTarget("X7") + stim.target_x(7) + >>> stim.GateTarget("rec[-3]") + stim.target_rec(-3) + >>> stim.GateTarget("!Z7") + stim.target_z(7, invert=True) + >>> stim.GateTarget("*") + stim.GateTarget.combiner() """ ``` @@ -9555,7 +9990,6 @@ def is_sweep_bit_target( ) -> bool: """Returns whether or not this is a sweep bit target like `sweep[4]`. - Examples: >>> import stim >>> stim.GateTarget(6).is_sweep_bit_target @@ -9776,6 +10210,14 @@ class GateTargetWithCoords: problem in a circuit, instead of having to constantly manually look up the coordinates of a qubit index in order to understand what is happening. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) + >>> t.coords + [1.5, 2.0] """ ``` @@ -9790,6 +10232,14 @@ def __init__( coords: List[float], ) -> None: """Creates a stim.GateTargetWithCoords. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) + >>> t.coords + [1.5, 2.0] """ ``` @@ -9805,6 +10255,12 @@ def coords( """Returns the associated coordinate information as a list of floats. If there is no coordinate information, returns an empty list. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.coords + [1.5, 2.0] """ ``` @@ -9818,6 +10274,12 @@ def gate_target( self, ) -> stim.GateTarget: """Returns the actual gate target as a `stim.GateTarget`. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) """ ``` @@ -13290,6 +13752,18 @@ def c_xyz( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.c_xyz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y +Z +X """ ``` @@ -13306,6 +13780,18 @@ def c_zyx( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.c_zyx(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z +X +Y """ ``` @@ -13380,6 +13866,18 @@ def cnot( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cnot(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ ``` @@ -13552,6 +14050,18 @@ def cx( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ ``` @@ -13570,6 +14080,18 @@ def cy( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X """ ``` @@ -13588,6 +14110,18 @@ def cz( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ ``` @@ -13607,6 +14141,11 @@ def depolarize1( *targets: The indices of the qubits to target with the noise. p: The chance of the error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.depolarize1(0, 1, 2, p=0.01) """ ``` @@ -13628,6 +14167,11 @@ def depolarize2( zip(targets[::1], targets[1::2]). p: The chance of the error being applied, independently, to each qubit pair. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.depolarize1(0, 1, 4, 5, p=0.01) """ ``` @@ -13775,6 +14319,18 @@ def h( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z -Y +X """ ``` @@ -13791,6 +14347,18 @@ def h_xy( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_xy(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y +X -Z """ ``` @@ -13807,6 +14375,18 @@ def h_xz( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_xz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z -Y +X """ ``` @@ -13823,6 +14403,18 @@ def h_yz( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_yz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X +Z +Y """ ``` @@ -13841,6 +14433,18 @@ def iswap( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.iswap(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Y +Z """ ``` @@ -13859,6 +14463,18 @@ def iswap_dag( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.iswap_dag(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ -Y +Z """ ``` @@ -13884,6 +14500,15 @@ def measure( Returns: The measurement result as a bool. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x(1) + >>> s.measure(0) + False + >>> s.measure(1) + True """ ``` @@ -13965,6 +14590,13 @@ def measure_many( Returns: The measurement results as a list of bools. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x(1) + >>> s.measure_many(0, 1) + [False, True] """ ``` @@ -14550,6 +15182,18 @@ def s( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.s(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y -X +Z """ ``` @@ -14566,6 +15210,18 @@ def s_dag( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.s_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -Y +X +Z """ ``` @@ -14827,6 +15483,18 @@ def sqrt_x( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_x(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Z -Y """ ``` @@ -14843,6 +15511,18 @@ def sqrt_x_dag( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_x_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X -Z +Y """ ``` @@ -14859,6 +15539,18 @@ def sqrt_y( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_y(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -Z +Y +X """ ``` @@ -14875,6 +15567,18 @@ def sqrt_y_dag( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_y_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z +Y -X """ ``` @@ -14951,6 +15655,18 @@ def swap( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.swap(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +Y +X +X +Z """ ``` @@ -14967,6 +15683,18 @@ def x( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.x(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X -Y -Z """ ``` @@ -14986,6 +15714,11 @@ def x_error( *targets: The indices of the qubits to target with the noise. p: The chance of the X error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x_error(0, 1, 2, p=0.01) """ ``` @@ -15004,6 +15737,18 @@ def xcx( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X """ ``` @@ -15022,6 +15767,18 @@ def xcy( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ """ ``` @@ -15040,6 +15797,18 @@ def xcz( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ """ ``` @@ -15056,6 +15825,18 @@ def y( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.y(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X +Y -Z """ ``` @@ -15075,6 +15856,11 @@ def y_error( *targets: The indices of the qubits to target with the noise. p: The chance of the Y error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.y_error(0, 1, 2, p=0.01) """ ``` @@ -15093,6 +15879,18 @@ def ycx( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ ``` @@ -15111,6 +15909,18 @@ def ycy( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ """ ``` @@ -15129,6 +15939,18 @@ def ycz( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +_ +_ """ ``` @@ -15145,6 +15967,18 @@ def z( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.z(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X -Y +Z """ ``` @@ -15164,6 +15998,11 @@ def y_error( *targets: The indices of the qubits to target with the noise. p: The chance of the Z error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.z_error(0, 1, 2, p=0.01) """ ``` @@ -15182,6 +16021,18 @@ def zcx( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ ``` @@ -15200,6 +16051,18 @@ def zcy( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X """ ``` @@ -15218,6 +16081,18 @@ def zcz( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ ``` diff --git a/doc/stim.pyi b/doc/stim.pyi index 059bc23e3..406b3c7e0 100644 --- a/doc/stim.pyi +++ b/doc/stim.pyi @@ -3332,6 +3332,17 @@ class CircuitRepeatBlock: repeat_count: The number of times to repeat the block. body: The body of the block, as a circuit. tag: Defaults to empty. A custom string attached to the REPEAT instruction. + + Examples: + >>> import stim + >>> c = stim.Circuit() + >>> c.append(stim.CircuitRepeatBlock(100, stim.Circuit("M 0"))) + >>> c + stim.Circuit(''' + REPEAT 100 { + M 0 + } + ''') """ def __ne__( self, @@ -3462,18 +3473,54 @@ class CircuitTargetsInsideInstruction: targets_in_range: List[stim.GateTargetWithCoords], ) -> None: """Creates a stim.CircuitTargetsInsideInstruction. + + Examples: + >>> import stim + >>> val = stim.CircuitTargetsInsideInstruction( + ... gate='X_ERROR', + ... args=[0.25], + ... target_range_start=0, + ... target_range_end=1, + ... targets_in_range=[stim.GateTargetWithCoords(0, [])], + ... ) """ @property def args( self, ) -> List[float]: """Returns parens arguments of the gate / instruction that was being executed. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.args + [0.25] """ @property def gate( self, ) -> Optional[str]: """Returns the name of the gate / instruction that was being executed. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.gate + 'X_ERROR' """ @property def target_range_end( @@ -3481,6 +3528,21 @@ class CircuitTargetsInsideInstruction: ) -> int: """Returns the exclusive end of the range of targets that were executing within the gate / instruction. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.target_range_start + 0 + >>> loc.instruction_targets.target_range_end + 1 """ @property def target_range_start( @@ -3488,6 +3550,21 @@ class CircuitTargetsInsideInstruction: ) -> int: """Returns the inclusive start of the range of targets that were executing within the gate / instruction. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.target_range_start + 0 + >>> loc.instruction_targets.target_range_end + 1 """ @property def targets_in_range( @@ -3496,6 +3573,19 @@ class CircuitTargetsInsideInstruction: """Returns the subset of targets of the gate/instruction that were being executed. Includes coordinate data with the targets. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.targets_in_range + [stim.GateTargetWithCoords(0, [])] """ class CompiledDemSampler: """A helper class for efficiently sampler from a detector error model. @@ -4835,10 +4925,17 @@ class DemTargetWithCoords: problem in a circuit, instead of having to constantly manually look up the coordinates of a detector index in order to understand what is happening. + + Examples: + >>> import stim + >>> t = stim.DemTargetWithCoords(stim.DemTarget("D1"), [1.5, 2.0]) + >>> t.dem_target + stim.DemTarget('D1') + >>> t.coords + [1.5, 2.0] """ def __init__( self, - *, dem_target: stim.DemTarget, coords: List[float], ) -> None: @@ -5802,6 +5899,28 @@ class DetectorErrorModel: """ class ExplainedError: """Describes the location of an error mechanism from a stim circuit. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0]) + ExplainedError { + dem_error_terms: L0 + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } + } """ def __init__( self, @@ -5810,6 +5929,28 @@ class ExplainedError: circuit_error_locations: List[stim.CircuitErrorLocation], ) -> None: """Creates a stim.ExplainedError. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0]) + ExplainedError { + dem_error_terms: L0 + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } + } """ @property def circuit_error_locations( @@ -5824,6 +5965,25 @@ class ExplainedError: Note: if this list is empty, it may be because there was a DEM error decomposed into parts where one of the parts is impossible to make on its own from a single circuit error. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0].circuit_error_locations[0]) + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } """ @property def dem_error_terms( @@ -6133,6 +6293,69 @@ class FlipSimulator: >>> sim.peek_pauli_flips() [stim.PauliString("+YX__")] """ + def generate_bernoulli_samples( + self, + num_samples: int, + *, + p: float, + bit_packed: bool = False, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + """Uses the simulator's random number generator to produce biased coin flips. + + This method has best performance when specifying `bit_packed=True` and + when specifying an `out=` parameter pointing to a numpy array that has + contiguous data aligned to a 64 bit boundary. (If `out` isn't specified, + the returned numpy array will have this property.) + + Args: + num_samples: The number of samples to produce. + p: The probability of each sample being True instead of False. + bit_packed: Defaults to False (no bit packing). When True, the result + has type np.uint8 instead of np.bool_ and 8 samples are packed into + each byte as if by np.packbits(bitorder='little'). (The bit order + is relevant when producing a number of samples that isn't a multiple + of 8.) + out: Defaults to None (allocate new). A numpy array to write the samples + into. Must have the correct size and dtype. + + Returns: + A numpy array containing the samples. The shape and dtype depends on + the bit_packed argument: + + if not bit_packed: + shape = (num_samples,) + dtype = np.bool_ + elif not transpose and bit_packed: + shape = (math.ceil(num_samples / 8),) + dtype = np.uint8 + + Raises: + ValueError: + The given `out` argument had a shape or dtype inconsistent with the + requested data. + + Examples: + >>> import stim + >>> sim = stim.FlipSimulator(batch_size=256) + >>> r = sim.generate_bernoulli_samples(1001, p=0.25) + >>> r.dtype + dtype('bool') + >>> r.shape + (1001,) + + >>> r = sim.generate_bernoulli_samples(53, p=0.1, bit_packed=True) + >>> r.dtype + dtype('uint8') + >>> r.shape + (7,) + >>> r[6] & 0b1110_0000 # zero'd padding bits + np.uint8(0) + + >>> r2 = sim.generate_bernoulli_samples(53, p=0.2, bit_packed=True, out=r) + >>> r is r2 # Check request to reuse r worked. + True + """ def get_detector_flips( self, *, @@ -6498,11 +6721,175 @@ class FlipSimulator: >>> sim.peek_pauli_flips() [stim.PauliString("+___"), stim.PauliString("+__X")] """ + def to_numpy( + self, + *, + bit_packed: bool = False, + transpose: bool = False, + output_xs: Union[bool, np.ndarray] = False, + output_zs: Union[bool, np.ndarray] = False, + output_measure_flips: Union[bool, np.ndarray] = False, + output_detector_flips: Union[bool, np.ndarray] = False, + output_observable_flips: Union[bool, np.ndarray] = False, + ) -> Optional[Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]]: + """Writes the simulator state into numpy arrays. + + Args: + bit_packed: Whether or not the result is bit packed, storing 8 bits per + byte instead of 1 bit per byte. Bit packing always applies to + the second index of the result. Bits are packed in little endian + order (as if by `np.packbits(X, axis=1, order='little')`). + transpose: Defaults to False. When set to False, the second index of the + returned array (the index affected by bit packing) is the shot index + (meaning the first index is the qubit index or measurement index or + etc). When set to True, results are transposed so that the first + index is the shot index. + output_xs: Defaults to False. When set to False, the X flip data is not + generated and the corresponding array in the result tuple is set to + None. When set to True, a new array is allocated to hold the X flip + data and this array is returned via the result tuple. When set to + a numpy array, the results are written into that array (the shape and + dtype of the array must be exactly correct). + output_zs: Defaults to False. When set to False, the Z flip data is not + generated and the corresponding array in the result tuple is set to + None. When set to True, a new array is allocated to hold the Z flip + data and this array is returned via the result tuple. When set to + a numpy array, the results are written into that array (the shape and + dtype of the array must be exactly correct). + output_measure_flips: Defaults to False. When set to False, the measure + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the measure flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + output_detector_flips: Defaults to False. When set to False, the detector + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the detector flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + output_observable_flips: Defaults to False. When set to False, the obs + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the obs flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + + Returns: + A tuple (xs, zs, ms, ds, os) of numpy arrays. The xs and zs arrays are + the pauli flip data specified using XZ encoding (00=I, 10=X, 11=Y, 01=Z). + The ms array is the measure flip data, the ds array is the detector flip + data, and the os array is the obs flip data. The arrays default to + `None` when the corresponding `output_*` argument was left False. + + The shape and dtype of the data depends on arguments given to the function. + The following specifies each array's shape and dtype for each case: + + if not transpose and not bit_packed: + xs.shape = (sim.batch_size, sim.num_qubits) + zs.shape = (sim.batch_size, sim.num_qubits) + ms.shape = (sim.batch_size, sim.num_measurements) + ds.shape = (sim.batch_size, sim.num_detectors) + os.shape = (sim.batch_size, sim.num_observables) + xs.dtype = np.bool_ + zs.dtype = np.bool_ + ms.dtype = np.bool_ + ds.dtype = np.bool_ + os.dtype = np.bool_ + elif not transpose and bit_packed: + xs.shape = (sim.batch_size, math.ceil(sim.num_qubits / 8)) + zs.shape = (sim.batch_size, math.ceil(sim.num_qubits / 8)) + ms.shape = (sim.batch_size, math.ceil(sim.num_measurements / 8)) + ds.shape = (sim.batch_size, math.ceil(sim.num_detectors / 8)) + os.shape = (sim.batch_size, math.ceil(sim.num_observables / 8)) + xs.dtype = np.uint8 + zs.dtype = np.uint8 + ms.dtype = np.uint8 + ds.dtype = np.uint8 + os.dtype = np.uint8 + elif transpose and not bit_packed: + xs.shape = (sim.num_qubits, sim.batch_size) + zs.shape = (sim.num_qubits, sim.batch_size) + ms.shape = (sim.num_measurements, sim.batch_size) + ds.shape = (sim.num_detectors, sim.batch_size) + os.shape = (sim.num_observables, sim.batch_size) + xs.dtype = np.bool_ + zs.dtype = np.bool_ + ms.dtype = np.bool_ + ds.dtype = np.bool_ + os.dtype = np.bool_ + elif transpose and bit_packed: + xs.shape = (sim.num_qubits, math.ceil(sim.batch_size / 8)) + zs.shape = (sim.num_qubits, math.ceil(sim.batch_size / 8)) + ms.shape = (sim.num_measurements, math.ceil(sim.batch_size / 8)) + ds.shape = (sim.num_detectors, math.ceil(sim.batch_size / 8)) + os.shape = (sim.num_observables, math.ceil(sim.batch_size / 8)) + xs.dtype = np.uint8 + zs.dtype = np.uint8 + ms.dtype = np.uint8 + ds.dtype = np.uint8 + os.dtype = np.uint8 + + Raises: + ValueError: + All the `output_*` arguments were False, or an `output_*` argument + had a shape or dtype inconsistent with the requested data. + + Examples: + >>> import stim + >>> import numpy as np + >>> sim = stim.FlipSimulator(batch_size=9) + >>> sim.do(stim.Circuit('M(1) 0 1 2')) + + >>> ms_buf = np.empty(shape=(9, 1), dtype=np.uint8) + >>> xs, zs, ms, ds, os = sim.to_numpy( + ... transpose=True, + ... bit_packed=True, + ... output_xs=True, + ... output_measure_flips=ms_buf, + ... ) + >>> assert ms is ms_buf + >>> xs + array([[0], + [0], + [0], + [0], + [0], + [0], + [0], + [0], + [0]], dtype=uint8) + >>> zs + >>> ms + array([[7], + [7], + [7], + [7], + [7], + [7], + [7], + [7], + [7]], dtype=uint8) + >>> ds + >>> os + """ class FlippedMeasurement: """Describes a measurement that was flipped. Gives the measurement's index in the measurement record, and also the observable of the measurement. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement + stim.FlippedMeasurement( + record_index=1, + observable=(stim.GateTargetWithCoords(stim.target_z(10), []),), + ) """ def __init__( self, @@ -6529,6 +6916,15 @@ class FlippedMeasurement: """Returns the observable of the flipped measurement. For example, an `MX 5` measurement will have the observable X5. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement.observable + [stim.GateTargetWithCoords(stim.target_z(10), [])] """ @property def record_index( @@ -6537,6 +6933,15 @@ class FlippedMeasurement: """The measurement record index of the flipped measurement. For example, the fifth measurement in a circuit has a measurement record index of 4. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement.record_index + 1 """ class Flow: """A stabilizer flow (e.g. "XI -> XX xor rec[-1]"). @@ -7379,7 +7784,21 @@ class GateTarget: """Initializes a `stim.GateTarget`. Args: - value: A target like `5` or `stim.target_rec(-1)`. + value: A value to convert into a gate target, like an integer + to interpret as a qubit target or a string to parse. + + Examples: + >>> import stim + >>> stim.GateTarget(stim.GateTarget(5)) + stim.GateTarget(5) + >>> stim.GateTarget("X7") + stim.target_x(7) + >>> stim.GateTarget("rec[-3]") + stim.target_rec(-3) + >>> stim.GateTarget("!Z7") + stim.target_z(7, invert=True) + >>> stim.GateTarget("*") + stim.GateTarget.combiner() """ def __ne__( self, @@ -7494,7 +7913,6 @@ class GateTarget: ) -> bool: """Returns whether or not this is a sweep bit target like `sweep[4]`. - Examples: >>> import stim >>> stim.GateTarget(6).is_sweep_bit_target @@ -7666,6 +8084,14 @@ class GateTargetWithCoords: problem in a circuit, instead of having to constantly manually look up the coordinates of a qubit index in order to understand what is happening. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) + >>> t.coords + [1.5, 2.0] """ def __init__( self, @@ -7673,6 +8099,14 @@ class GateTargetWithCoords: coords: List[float], ) -> None: """Creates a stim.GateTargetWithCoords. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) + >>> t.coords + [1.5, 2.0] """ @property def coords( @@ -7681,12 +8115,24 @@ class GateTargetWithCoords: """Returns the associated coordinate information as a list of floats. If there is no coordinate information, returns an empty list. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.coords + [1.5, 2.0] """ @property def gate_target( self, ) -> stim.GateTarget: """Returns the actual gate target as a `stim.GateTarget`. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) """ class PauliString: """A signed Pauli tensor product (e.g. "+X \u2297 X \u2297 X" or "-Y \u2297 Z". @@ -10549,6 +10995,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.c_xyz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y +Z +X """ def c_zyx( self, @@ -10558,6 +11016,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.c_zyx(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z +X +Y """ def canonical_stabilizers( self, @@ -10618,6 +11088,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cnot(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def copy( self, @@ -10762,6 +11244,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def cy( self, @@ -10773,6 +11267,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X """ def cz( self, @@ -10784,6 +11290,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def depolarize1( self, @@ -10796,6 +11314,11 @@ class TableauSimulator: *targets: The indices of the qubits to target with the noise. p: The chance of the error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.depolarize1(0, 1, 2, p=0.01) """ def depolarize2( self, @@ -10810,6 +11333,11 @@ class TableauSimulator: zip(targets[::1], targets[1::2]). p: The chance of the error being applied, independently, to each qubit pair. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.depolarize1(0, 1, 4, 5, p=0.01) """ def do( self, @@ -10922,6 +11450,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z -Y +X """ def h_xy( self, @@ -10931,6 +11471,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_xy(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y +X -Z """ def h_xz( self, @@ -10940,6 +11492,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_xz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z -Y +X """ def h_yz( self, @@ -10949,6 +11513,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_yz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X +Z +Y """ def iswap( self, @@ -10960,6 +11536,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.iswap(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Y +Z """ def iswap_dag( self, @@ -10971,6 +11559,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.iswap_dag(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ -Y +Z """ def measure( self, @@ -10989,6 +11589,15 @@ class TableauSimulator: Returns: The measurement result as a bool. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x(1) + >>> s.measure(0) + False + >>> s.measure(1) + True """ def measure_kickback( self, @@ -11056,6 +11665,13 @@ class TableauSimulator: Returns: The measurement results as a list of bools. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x(1) + >>> s.measure_many(0, 1) + [False, True] """ def measure_observable( self, @@ -11529,6 +12145,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.s(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y -X +Z """ def s_dag( self, @@ -11538,6 +12166,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.s_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -Y +X +Z """ def set_inverse_tableau( self, @@ -11764,6 +12404,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_x(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Z -Y """ def sqrt_x_dag( self, @@ -11773,6 +12425,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_x_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X -Z +Y """ def sqrt_y( self, @@ -11782,6 +12446,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_y(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -Z +Y +X """ def sqrt_y_dag( self, @@ -11791,6 +12467,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_y_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z +Y -X """ def state_vector( self, @@ -11853,6 +12541,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.swap(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +Y +X +X +Z """ def x( self, @@ -11862,6 +12562,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.x(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X -Y -Z """ def x_error( self, @@ -11874,6 +12586,11 @@ class TableauSimulator: *targets: The indices of the qubits to target with the noise. p: The chance of the X error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x_error(0, 1, 2, p=0.01) """ def xcx( self, @@ -11885,6 +12602,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X """ def xcy( self, @@ -11896,6 +12625,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ """ def xcz( self, @@ -11907,6 +12648,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ """ def y( self, @@ -11916,6 +12669,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.y(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X +Y -Z """ def y_error( self, @@ -11928,6 +12693,11 @@ class TableauSimulator: *targets: The indices of the qubits to target with the noise. p: The chance of the Y error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.y_error(0, 1, 2, p=0.01) """ def ycx( self, @@ -11939,6 +12709,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def ycy( self, @@ -11950,6 +12732,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ """ def ycz( self, @@ -11961,6 +12755,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +_ +_ """ def z( self, @@ -11970,6 +12776,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.z(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X -Y +Z """ def y_error( self, @@ -11982,6 +12800,11 @@ class TableauSimulator: *targets: The indices of the qubits to target with the noise. p: The chance of the Z error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.z_error(0, 1, 2, p=0.01) """ def zcx( self, @@ -11993,6 +12816,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def zcy( self, @@ -12004,6 +12839,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X """ def zcz( self, @@ -12015,6 +12862,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ @overload def gate_data( diff --git a/glue/python/src/stim/__init__.pyi b/glue/python/src/stim/__init__.pyi index 059bc23e3..406b3c7e0 100644 --- a/glue/python/src/stim/__init__.pyi +++ b/glue/python/src/stim/__init__.pyi @@ -3332,6 +3332,17 @@ class CircuitRepeatBlock: repeat_count: The number of times to repeat the block. body: The body of the block, as a circuit. tag: Defaults to empty. A custom string attached to the REPEAT instruction. + + Examples: + >>> import stim + >>> c = stim.Circuit() + >>> c.append(stim.CircuitRepeatBlock(100, stim.Circuit("M 0"))) + >>> c + stim.Circuit(''' + REPEAT 100 { + M 0 + } + ''') """ def __ne__( self, @@ -3462,18 +3473,54 @@ class CircuitTargetsInsideInstruction: targets_in_range: List[stim.GateTargetWithCoords], ) -> None: """Creates a stim.CircuitTargetsInsideInstruction. + + Examples: + >>> import stim + >>> val = stim.CircuitTargetsInsideInstruction( + ... gate='X_ERROR', + ... args=[0.25], + ... target_range_start=0, + ... target_range_end=1, + ... targets_in_range=[stim.GateTargetWithCoords(0, [])], + ... ) """ @property def args( self, ) -> List[float]: """Returns parens arguments of the gate / instruction that was being executed. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.args + [0.25] """ @property def gate( self, ) -> Optional[str]: """Returns the name of the gate / instruction that was being executed. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.gate + 'X_ERROR' """ @property def target_range_end( @@ -3481,6 +3528,21 @@ class CircuitTargetsInsideInstruction: ) -> int: """Returns the exclusive end of the range of targets that were executing within the gate / instruction. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.target_range_start + 0 + >>> loc.instruction_targets.target_range_end + 1 """ @property def target_range_start( @@ -3488,6 +3550,21 @@ class CircuitTargetsInsideInstruction: ) -> int: """Returns the inclusive start of the range of targets that were executing within the gate / instruction. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.target_range_start + 0 + >>> loc.instruction_targets.target_range_end + 1 """ @property def targets_in_range( @@ -3496,6 +3573,19 @@ class CircuitTargetsInsideInstruction: """Returns the subset of targets of the gate/instruction that were being executed. Includes coordinate data with the targets. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.targets_in_range + [stim.GateTargetWithCoords(0, [])] """ class CompiledDemSampler: """A helper class for efficiently sampler from a detector error model. @@ -4835,10 +4925,17 @@ class DemTargetWithCoords: problem in a circuit, instead of having to constantly manually look up the coordinates of a detector index in order to understand what is happening. + + Examples: + >>> import stim + >>> t = stim.DemTargetWithCoords(stim.DemTarget("D1"), [1.5, 2.0]) + >>> t.dem_target + stim.DemTarget('D1') + >>> t.coords + [1.5, 2.0] """ def __init__( self, - *, dem_target: stim.DemTarget, coords: List[float], ) -> None: @@ -5802,6 +5899,28 @@ class DetectorErrorModel: """ class ExplainedError: """Describes the location of an error mechanism from a stim circuit. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0]) + ExplainedError { + dem_error_terms: L0 + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } + } """ def __init__( self, @@ -5810,6 +5929,28 @@ class ExplainedError: circuit_error_locations: List[stim.CircuitErrorLocation], ) -> None: """Creates a stim.ExplainedError. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0]) + ExplainedError { + dem_error_terms: L0 + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } + } """ @property def circuit_error_locations( @@ -5824,6 +5965,25 @@ class ExplainedError: Note: if this list is empty, it may be because there was a DEM error decomposed into parts where one of the parts is impossible to make on its own from a single circuit error. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0].circuit_error_locations[0]) + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } """ @property def dem_error_terms( @@ -6133,6 +6293,69 @@ class FlipSimulator: >>> sim.peek_pauli_flips() [stim.PauliString("+YX__")] """ + def generate_bernoulli_samples( + self, + num_samples: int, + *, + p: float, + bit_packed: bool = False, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + """Uses the simulator's random number generator to produce biased coin flips. + + This method has best performance when specifying `bit_packed=True` and + when specifying an `out=` parameter pointing to a numpy array that has + contiguous data aligned to a 64 bit boundary. (If `out` isn't specified, + the returned numpy array will have this property.) + + Args: + num_samples: The number of samples to produce. + p: The probability of each sample being True instead of False. + bit_packed: Defaults to False (no bit packing). When True, the result + has type np.uint8 instead of np.bool_ and 8 samples are packed into + each byte as if by np.packbits(bitorder='little'). (The bit order + is relevant when producing a number of samples that isn't a multiple + of 8.) + out: Defaults to None (allocate new). A numpy array to write the samples + into. Must have the correct size and dtype. + + Returns: + A numpy array containing the samples. The shape and dtype depends on + the bit_packed argument: + + if not bit_packed: + shape = (num_samples,) + dtype = np.bool_ + elif not transpose and bit_packed: + shape = (math.ceil(num_samples / 8),) + dtype = np.uint8 + + Raises: + ValueError: + The given `out` argument had a shape or dtype inconsistent with the + requested data. + + Examples: + >>> import stim + >>> sim = stim.FlipSimulator(batch_size=256) + >>> r = sim.generate_bernoulli_samples(1001, p=0.25) + >>> r.dtype + dtype('bool') + >>> r.shape + (1001,) + + >>> r = sim.generate_bernoulli_samples(53, p=0.1, bit_packed=True) + >>> r.dtype + dtype('uint8') + >>> r.shape + (7,) + >>> r[6] & 0b1110_0000 # zero'd padding bits + np.uint8(0) + + >>> r2 = sim.generate_bernoulli_samples(53, p=0.2, bit_packed=True, out=r) + >>> r is r2 # Check request to reuse r worked. + True + """ def get_detector_flips( self, *, @@ -6498,11 +6721,175 @@ class FlipSimulator: >>> sim.peek_pauli_flips() [stim.PauliString("+___"), stim.PauliString("+__X")] """ + def to_numpy( + self, + *, + bit_packed: bool = False, + transpose: bool = False, + output_xs: Union[bool, np.ndarray] = False, + output_zs: Union[bool, np.ndarray] = False, + output_measure_flips: Union[bool, np.ndarray] = False, + output_detector_flips: Union[bool, np.ndarray] = False, + output_observable_flips: Union[bool, np.ndarray] = False, + ) -> Optional[Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]]: + """Writes the simulator state into numpy arrays. + + Args: + bit_packed: Whether or not the result is bit packed, storing 8 bits per + byte instead of 1 bit per byte. Bit packing always applies to + the second index of the result. Bits are packed in little endian + order (as if by `np.packbits(X, axis=1, order='little')`). + transpose: Defaults to False. When set to False, the second index of the + returned array (the index affected by bit packing) is the shot index + (meaning the first index is the qubit index or measurement index or + etc). When set to True, results are transposed so that the first + index is the shot index. + output_xs: Defaults to False. When set to False, the X flip data is not + generated and the corresponding array in the result tuple is set to + None. When set to True, a new array is allocated to hold the X flip + data and this array is returned via the result tuple. When set to + a numpy array, the results are written into that array (the shape and + dtype of the array must be exactly correct). + output_zs: Defaults to False. When set to False, the Z flip data is not + generated and the corresponding array in the result tuple is set to + None. When set to True, a new array is allocated to hold the Z flip + data and this array is returned via the result tuple. When set to + a numpy array, the results are written into that array (the shape and + dtype of the array must be exactly correct). + output_measure_flips: Defaults to False. When set to False, the measure + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the measure flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + output_detector_flips: Defaults to False. When set to False, the detector + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the detector flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + output_observable_flips: Defaults to False. When set to False, the obs + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the obs flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + + Returns: + A tuple (xs, zs, ms, ds, os) of numpy arrays. The xs and zs arrays are + the pauli flip data specified using XZ encoding (00=I, 10=X, 11=Y, 01=Z). + The ms array is the measure flip data, the ds array is the detector flip + data, and the os array is the obs flip data. The arrays default to + `None` when the corresponding `output_*` argument was left False. + + The shape and dtype of the data depends on arguments given to the function. + The following specifies each array's shape and dtype for each case: + + if not transpose and not bit_packed: + xs.shape = (sim.batch_size, sim.num_qubits) + zs.shape = (sim.batch_size, sim.num_qubits) + ms.shape = (sim.batch_size, sim.num_measurements) + ds.shape = (sim.batch_size, sim.num_detectors) + os.shape = (sim.batch_size, sim.num_observables) + xs.dtype = np.bool_ + zs.dtype = np.bool_ + ms.dtype = np.bool_ + ds.dtype = np.bool_ + os.dtype = np.bool_ + elif not transpose and bit_packed: + xs.shape = (sim.batch_size, math.ceil(sim.num_qubits / 8)) + zs.shape = (sim.batch_size, math.ceil(sim.num_qubits / 8)) + ms.shape = (sim.batch_size, math.ceil(sim.num_measurements / 8)) + ds.shape = (sim.batch_size, math.ceil(sim.num_detectors / 8)) + os.shape = (sim.batch_size, math.ceil(sim.num_observables / 8)) + xs.dtype = np.uint8 + zs.dtype = np.uint8 + ms.dtype = np.uint8 + ds.dtype = np.uint8 + os.dtype = np.uint8 + elif transpose and not bit_packed: + xs.shape = (sim.num_qubits, sim.batch_size) + zs.shape = (sim.num_qubits, sim.batch_size) + ms.shape = (sim.num_measurements, sim.batch_size) + ds.shape = (sim.num_detectors, sim.batch_size) + os.shape = (sim.num_observables, sim.batch_size) + xs.dtype = np.bool_ + zs.dtype = np.bool_ + ms.dtype = np.bool_ + ds.dtype = np.bool_ + os.dtype = np.bool_ + elif transpose and bit_packed: + xs.shape = (sim.num_qubits, math.ceil(sim.batch_size / 8)) + zs.shape = (sim.num_qubits, math.ceil(sim.batch_size / 8)) + ms.shape = (sim.num_measurements, math.ceil(sim.batch_size / 8)) + ds.shape = (sim.num_detectors, math.ceil(sim.batch_size / 8)) + os.shape = (sim.num_observables, math.ceil(sim.batch_size / 8)) + xs.dtype = np.uint8 + zs.dtype = np.uint8 + ms.dtype = np.uint8 + ds.dtype = np.uint8 + os.dtype = np.uint8 + + Raises: + ValueError: + All the `output_*` arguments were False, or an `output_*` argument + had a shape or dtype inconsistent with the requested data. + + Examples: + >>> import stim + >>> import numpy as np + >>> sim = stim.FlipSimulator(batch_size=9) + >>> sim.do(stim.Circuit('M(1) 0 1 2')) + + >>> ms_buf = np.empty(shape=(9, 1), dtype=np.uint8) + >>> xs, zs, ms, ds, os = sim.to_numpy( + ... transpose=True, + ... bit_packed=True, + ... output_xs=True, + ... output_measure_flips=ms_buf, + ... ) + >>> assert ms is ms_buf + >>> xs + array([[0], + [0], + [0], + [0], + [0], + [0], + [0], + [0], + [0]], dtype=uint8) + >>> zs + >>> ms + array([[7], + [7], + [7], + [7], + [7], + [7], + [7], + [7], + [7]], dtype=uint8) + >>> ds + >>> os + """ class FlippedMeasurement: """Describes a measurement that was flipped. Gives the measurement's index in the measurement record, and also the observable of the measurement. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement + stim.FlippedMeasurement( + record_index=1, + observable=(stim.GateTargetWithCoords(stim.target_z(10), []),), + ) """ def __init__( self, @@ -6529,6 +6916,15 @@ class FlippedMeasurement: """Returns the observable of the flipped measurement. For example, an `MX 5` measurement will have the observable X5. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement.observable + [stim.GateTargetWithCoords(stim.target_z(10), [])] """ @property def record_index( @@ -6537,6 +6933,15 @@ class FlippedMeasurement: """The measurement record index of the flipped measurement. For example, the fifth measurement in a circuit has a measurement record index of 4. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement.record_index + 1 """ class Flow: """A stabilizer flow (e.g. "XI -> XX xor rec[-1]"). @@ -7379,7 +7784,21 @@ class GateTarget: """Initializes a `stim.GateTarget`. Args: - value: A target like `5` or `stim.target_rec(-1)`. + value: A value to convert into a gate target, like an integer + to interpret as a qubit target or a string to parse. + + Examples: + >>> import stim + >>> stim.GateTarget(stim.GateTarget(5)) + stim.GateTarget(5) + >>> stim.GateTarget("X7") + stim.target_x(7) + >>> stim.GateTarget("rec[-3]") + stim.target_rec(-3) + >>> stim.GateTarget("!Z7") + stim.target_z(7, invert=True) + >>> stim.GateTarget("*") + stim.GateTarget.combiner() """ def __ne__( self, @@ -7494,7 +7913,6 @@ class GateTarget: ) -> bool: """Returns whether or not this is a sweep bit target like `sweep[4]`. - Examples: >>> import stim >>> stim.GateTarget(6).is_sweep_bit_target @@ -7666,6 +8084,14 @@ class GateTargetWithCoords: problem in a circuit, instead of having to constantly manually look up the coordinates of a qubit index in order to understand what is happening. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) + >>> t.coords + [1.5, 2.0] """ def __init__( self, @@ -7673,6 +8099,14 @@ class GateTargetWithCoords: coords: List[float], ) -> None: """Creates a stim.GateTargetWithCoords. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) + >>> t.coords + [1.5, 2.0] """ @property def coords( @@ -7681,12 +8115,24 @@ class GateTargetWithCoords: """Returns the associated coordinate information as a list of floats. If there is no coordinate information, returns an empty list. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.coords + [1.5, 2.0] """ @property def gate_target( self, ) -> stim.GateTarget: """Returns the actual gate target as a `stim.GateTarget`. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) """ class PauliString: """A signed Pauli tensor product (e.g. "+X \u2297 X \u2297 X" or "-Y \u2297 Z". @@ -10549,6 +10995,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.c_xyz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y +Z +X """ def c_zyx( self, @@ -10558,6 +11016,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.c_zyx(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z +X +Y """ def canonical_stabilizers( self, @@ -10618,6 +11088,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cnot(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def copy( self, @@ -10762,6 +11244,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def cy( self, @@ -10773,6 +11267,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X """ def cz( self, @@ -10784,6 +11290,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def depolarize1( self, @@ -10796,6 +11314,11 @@ class TableauSimulator: *targets: The indices of the qubits to target with the noise. p: The chance of the error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.depolarize1(0, 1, 2, p=0.01) """ def depolarize2( self, @@ -10810,6 +11333,11 @@ class TableauSimulator: zip(targets[::1], targets[1::2]). p: The chance of the error being applied, independently, to each qubit pair. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.depolarize1(0, 1, 4, 5, p=0.01) """ def do( self, @@ -10922,6 +11450,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z -Y +X """ def h_xy( self, @@ -10931,6 +11471,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_xy(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y +X -Z """ def h_xz( self, @@ -10940,6 +11492,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_xz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z -Y +X """ def h_yz( self, @@ -10949,6 +11513,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_yz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X +Z +Y """ def iswap( self, @@ -10960,6 +11536,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.iswap(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Y +Z """ def iswap_dag( self, @@ -10971,6 +11559,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.iswap_dag(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ -Y +Z """ def measure( self, @@ -10989,6 +11589,15 @@ class TableauSimulator: Returns: The measurement result as a bool. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x(1) + >>> s.measure(0) + False + >>> s.measure(1) + True """ def measure_kickback( self, @@ -11056,6 +11665,13 @@ class TableauSimulator: Returns: The measurement results as a list of bools. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x(1) + >>> s.measure_many(0, 1) + [False, True] """ def measure_observable( self, @@ -11529,6 +12145,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.s(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y -X +Z """ def s_dag( self, @@ -11538,6 +12166,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.s_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -Y +X +Z """ def set_inverse_tableau( self, @@ -11764,6 +12404,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_x(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Z -Y """ def sqrt_x_dag( self, @@ -11773,6 +12425,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_x_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X -Z +Y """ def sqrt_y( self, @@ -11782,6 +12446,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_y(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -Z +Y +X """ def sqrt_y_dag( self, @@ -11791,6 +12467,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_y_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z +Y -X """ def state_vector( self, @@ -11853,6 +12541,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.swap(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +Y +X +X +Z """ def x( self, @@ -11862,6 +12562,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.x(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X -Y -Z """ def x_error( self, @@ -11874,6 +12586,11 @@ class TableauSimulator: *targets: The indices of the qubits to target with the noise. p: The chance of the X error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x_error(0, 1, 2, p=0.01) """ def xcx( self, @@ -11885,6 +12602,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X """ def xcy( self, @@ -11896,6 +12625,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ """ def xcz( self, @@ -11907,6 +12648,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ """ def y( self, @@ -11916,6 +12669,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.y(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X +Y -Z """ def y_error( self, @@ -11928,6 +12693,11 @@ class TableauSimulator: *targets: The indices of the qubits to target with the noise. p: The chance of the Y error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.y_error(0, 1, 2, p=0.01) """ def ycx( self, @@ -11939,6 +12709,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def ycy( self, @@ -11950,6 +12732,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ """ def ycz( self, @@ -11961,6 +12755,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +_ +_ """ def z( self, @@ -11970,6 +12776,18 @@ class TableauSimulator: Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.z(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X -Y +Z """ def y_error( self, @@ -11982,6 +12800,11 @@ class TableauSimulator: *targets: The indices of the qubits to target with the noise. p: The chance of the Z error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.z_error(0, 1, 2, p=0.01) """ def zcx( self, @@ -11993,6 +12816,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ def zcy( self, @@ -12004,6 +12839,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X """ def zcz( self, @@ -12015,6 +12862,18 @@ class TableauSimulator: *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X """ @overload def gate_data( diff --git a/src/stim/circuit/circuit.pybind.cc b/src/stim/circuit/circuit.pybind.cc index f6f133c26..4f3fc640f 100644 --- a/src/stim/circuit/circuit.pybind.cc +++ b/src/stim/circuit/circuit.pybind.cc @@ -169,12 +169,12 @@ std::vector py_find_undetectable_logical_error( return ErrorMatcher::explain_errors_from_circuit(self, &filter, reduce_to_representative); } -std::string py_shortest_error_sat_problem(const Circuit &self, std::string format) { +std::string py_shortest_error_sat_problem(const Circuit &self, std::string_view format) { DetectorErrorModel dem = ErrorAnalyzer::circuit_to_detector_error_model(self, false, true, false, 1, false, false); return stim::shortest_error_sat_problem(dem, format); } -std::string py_likeliest_error_sat_problem(const Circuit &self, int quantization, std::string format) { +std::string py_likeliest_error_sat_problem(const Circuit &self, int quantization, std::string_view format) { DetectorErrorModel dem = ErrorAnalyzer::circuit_to_detector_error_model(self, false, true, false, 1, false, false); return stim::likeliest_error_sat_problem(dem, quantization, format); } @@ -255,7 +255,7 @@ void circuit_append( std::vector raw_targets; try { raw_targets.push_back(obj_to_gate_target(targets).data); - } catch (const std::invalid_argument &ex) { + } catch (const std::invalid_argument &) { for (const auto &t : targets) { raw_targets.push_back(handle_to_gate_target(t).data); } @@ -279,13 +279,13 @@ void circuit_append( auto d = pybind11::cast(used_arg); self.safe_append_ua(gate_name, raw_targets, d, tag); return; - } catch (const pybind11::cast_error &ex) { + } catch (const pybind11::cast_error &) { } try { auto args = pybind11::cast>(used_arg); self.safe_append_u(gate_name, raw_targets, args, tag); return; - } catch (const pybind11::cast_error &ex) { + } catch (const pybind11::cast_error &) { } throw std::invalid_argument("Arg must be a double or sequence of doubles."); } else if (pybind11::isinstance(obj)) { @@ -1329,8 +1329,8 @@ void stim_pybind::pybind_circuit_methods(pybind11::module &, pybind11::class_ bool { try { return self.approx_equals(pybind11::cast(obj), atol); - } catch (const pybind11::cast_error &ex) { + } catch (const pybind11::cast_error &) { return false; } }, diff --git a/src/stim/circuit/circuit_repeat_block.pybind.cc b/src/stim/circuit/circuit_repeat_block.pybind.cc index f4f0bb782..49447d57d 100644 --- a/src/stim/circuit/circuit_repeat_block.pybind.cc +++ b/src/stim/circuit/circuit_repeat_block.pybind.cc @@ -85,6 +85,17 @@ void stim_pybind::pybind_circuit_repeat_block_methods(pybind11::module &m, pybin repeat_count: The number of times to repeat the block. body: The body of the block, as a circuit. tag: Defaults to empty. A custom string attached to the REPEAT instruction. + + Examples: + >>> import stim + >>> c = stim.Circuit() + >>> c.append(stim.CircuitRepeatBlock(100, stim.Circuit("M 0"))) + >>> c + stim.Circuit(''' + REPEAT 100 { + M 0 + } + ''') )DOC") .data()); diff --git a/src/stim/circuit/gate_target.pybind.cc b/src/stim/circuit/gate_target.pybind.cc index ba7db7f43..324951580 100644 --- a/src/stim/circuit/gate_target.pybind.cc +++ b/src/stim/circuit/gate_target.pybind.cc @@ -21,6 +21,11 @@ using namespace stim; using namespace stim_pybind; GateTarget handle_to_gate_target(const pybind11::handle &obj) { + try { + std::string_view text = pybind11::cast(obj); + return GateTarget::from_target_str(text); + } catch (const pybind11::cast_error &ex) { + } try { return pybind11::cast(obj); } catch (const pybind11::cast_error &ex) { @@ -65,7 +70,21 @@ void stim_pybind::pybind_circuit_gate_target_methods(pybind11::module &m, pybind Initializes a `stim.GateTarget`. Args: - value: A target like `5` or `stim.target_rec(-1)`. + value: A value to convert into a gate target, like an integer + to interpret as a qubit target or a string to parse. + + Examples: + >>> import stim + >>> stim.GateTarget(stim.GateTarget(5)) + stim.GateTarget(5) + >>> stim.GateTarget("X7") + stim.target_x(7) + >>> stim.GateTarget("rec[-3]") + stim.target_rec(-3) + >>> stim.GateTarget("!Z7") + stim.target_z(7, invert=True) + >>> stim.GateTarget("*") + stim.GateTarget.combiner() )DOC") .data()); @@ -340,7 +359,6 @@ void stim_pybind::pybind_circuit_gate_target_methods(pybind11::module &m, pybind clean_doc_string(R"DOC( Returns whether or not this is a sweep bit target like `sweep[4]`. - Examples: >>> import stim >>> stim.GateTarget(6).is_sweep_bit_target diff --git a/src/stim/py/numpy.pybind.cc b/src/stim/py/numpy.pybind.cc index 1aaecbe46..9f5e75fd2 100644 --- a/src/stim/py/numpy.pybind.cc +++ b/src/stim/py/numpy.pybind.cc @@ -123,12 +123,18 @@ static pybind11::object simd_bit_table_to_numpy_uint8( throw std::invalid_argument(ss.str()); } + uint8_t mask = 0b11111111; + if (num_minor & 7) { + mask = (1 << (num_minor & 7)) - 1; + } + if (num_major && num_minor) { auto stride = buf.strides(1); if (stride == 1) { for (size_t major = 0; major < num_major; major++) { auto row = table[major]; memcpy(buf.mutable_data(major, 0), row.u8, num_minor_bytes); + *buf.mutable_data(major, num_minor_bytes - 1) &= mask; } } else { for (size_t major = 0; major < num_major; major++) { @@ -138,6 +144,7 @@ static pybind11::object simd_bit_table_to_numpy_uint8( *ptr = row.u8[minor]; ptr += stride; } + *(ptr - stride) &= mask; } } } diff --git a/src/stim/search/sat/wcnf.cc b/src/stim/search/sat/wcnf.cc index aec6e423f..8f9577cfa 100644 --- a/src/stim/search/sat/wcnf.cc +++ b/src/stim/search/sat/wcnf.cc @@ -260,14 +260,14 @@ std::string sat_problem_as_wcnf_string(const DetectorErrorModel& model, bool wei } // Should ignore weights entirely and minimize the cardinality. -std::string stim::shortest_error_sat_problem(const DetectorErrorModel& model, std::string format) { +std::string stim::shortest_error_sat_problem(const DetectorErrorModel& model, std::string_view format) { if (format != "WDIMACS") { throw std::invalid_argument("Unsupported format."); } return sat_problem_as_wcnf_string(model, /*weighted=*/false, /*quantization=*/0); } -std::string stim::likeliest_error_sat_problem(const DetectorErrorModel& model, int quantization, std::string format) { +std::string stim::likeliest_error_sat_problem(const DetectorErrorModel& model, int quantization, std::string_view format) { if (format != "WDIMACS") { throw std::invalid_argument("Unsupported format."); } diff --git a/src/stim/search/sat/wcnf.h b/src/stim/search/sat/wcnf.h index e57d0d853..82c00cc3e 100644 --- a/src/stim/search/sat/wcnf.h +++ b/src/stim/search/sat/wcnf.h @@ -1,8 +1,6 @@ #ifndef _STIM_SEARCH_SAT_WCNF_H #define _STIM_SEARCH_SAT_WCNF_H -#include - #include "stim/dem/detector_error_model.h" namespace stim { @@ -33,13 +31,11 @@ namespace stim { /// users must separately manage the process of selecting and running the solver. This approach is designed to /// sidestep the need for direct integration with any particular solver and allow /// for experimentation with different solvers to achieve the best performance. -// std::string shortest_error_problem_as_wcnf_file( -// const DetectorErrorModel &model, bool weighted=false, size_t weight_scale_factor=0); -std::string shortest_error_sat_problem(const DetectorErrorModel& model, std::string format = "WDIMACS"); +std::string shortest_error_sat_problem(const DetectorErrorModel& model, std::string_view format = "WDIMACS"); std::string likeliest_error_sat_problem( - const DetectorErrorModel& model, int quantization = 10, std::string format = "WDIMACS"); + const DetectorErrorModel& model, int quantization = 10, std::string_view format = "WDIMACS"); } // namespace stim diff --git a/src/stim/simulators/frame_simulator.pybind.cc b/src/stim/simulators/frame_simulator.pybind.cc index abb686d80..0bcee6450 100644 --- a/src/stim/simulators/frame_simulator.pybind.cc +++ b/src/stim/simulators/frame_simulator.pybind.cc @@ -33,6 +33,141 @@ std::optional py_index_to_optional_size_t( return (size_t)i; } +static void generate_biased_samples_bit_packed_contiguous(uint8_t *out, size_t num_bytes, float p, std::mt19937_64 &rng) { + uintptr_t start = (uintptr_t)out; + uintptr_t end = start + num_bytes; + uintptr_t aligned64_start = start & ~0b111ULL; + uintptr_t aligned64_end = end & ~0b111ULL; + if (aligned64_start != start) { + aligned64_start += 8; + } + biased_randomize_bits(p, (uint64_t *)aligned64_start, (uint64_t *)aligned64_end, rng); + if (start < aligned64_start) { + uint64_t pad; + biased_randomize_bits(p, &pad, &pad + 1, rng); + for (size_t k = 0; k < aligned64_start - start; k++) { + out[k] = (uint8_t)(pad & 0xFF); + pad >>= 8; + } + } + if (aligned64_end < end) { + uint64_t pad; + biased_randomize_bits(p, &pad, &pad + 1, rng); + while (aligned64_end < end) { + *(uint8_t *)aligned64_end = (uint8_t)(pad & 0xFF); + aligned64_end++; + pad >>= 8; + } + } +} + +static void generate_biased_samples_bit_packed_with_stride(uint8_t *out, pybind11::ssize_t stride, size_t num_bytes, float p, std::mt19937_64 &rng) { + uint64_t stack[64]; + uint64_t *stack_ptr = &stack[0]; + for (size_t k1 = 0; k1 < num_bytes; k1 += 64*8) { + size_t n2 = std::min(num_bytes - k1, (size_t)(64*8)); + biased_randomize_bits(p, stack_ptr, stack_ptr + (n2 + 7) / 8, rng); + uint8_t *stack_data = (uint8_t *)(void *)stack_ptr; + for (size_t k2 = 0; k2 < n2; k2++) { + *out = *stack_data; + stack_data += 1; + out += stride; + } + } +} + +static void generate_biased_samples_bool(uint8_t *out, pybind11::ssize_t stride, size_t num_samples, float p, std::mt19937_64 &rng) { + uint64_t stack[64]; + uint64_t *stack_ptr = &stack[0]; + for (size_t k1 = 0; k1 < num_samples; k1 += 64*64) { + size_t n2 = std::min(num_samples - k1, (size_t)(64*64)); + biased_randomize_bits(p, stack_ptr, stack_ptr + (n2 + 63) / 64, rng); + for (size_t k2 = 0; k2 < n2; k2++) { + bool bit = (stack[k2 / 64] >> (k2 & 63)) & 1; + *out++ = bit; + } + } +} + +template +pybind11::object generate_bernoulli_samples(FrameSimulator &self, size_t num_samples, float p, bool bit_packed, pybind11::object out) { + if (bit_packed) { + size_t num_bytes = (num_samples + 7) / 8; + if (out.is_none()) { + // Allocate u64 aligned memory. + void *buffer = (void *)new uint64_t[(num_bytes + 7) / 8]; + pybind11::capsule free_when_done(buffer, [](void *f) { + delete[] reinterpret_cast(f); + }); + out = pybind11::array_t( + {(pybind11::ssize_t)num_bytes}, + {(pybind11::ssize_t)1}, + (uint8_t *)buffer, + free_when_done); + } else if (!pybind11::isinstance>(out)) { + throw std::invalid_argument("`out` wasn't `None` or a uint8 numpy array."); + } + auto buf = pybind11::cast>(out); + if (buf.ndim() != 1) { + throw std::invalid_argument("Output buffer wasn't one dimensional."); + } + if ((size_t)buf.shape(0) != num_bytes) { + std::stringstream ss; + ss << "Expected output buffer to have size " << num_bytes; + ss << " but its size is " << buf.shape(0) << "."; + throw std::invalid_argument(ss.str()); + } + auto stride = buf.strides(0); + void *start_of_data = (void *)buf.mutable_data(); + + if (stride == 1) { + generate_biased_samples_bit_packed_contiguous( + (uint8_t *)start_of_data, + num_bytes, + p, + self.rng); + } else { + generate_biased_samples_bit_packed_with_stride( + (uint8_t *)start_of_data, + stride, + num_bytes, + p, + self.rng); + } + if (num_samples & 0b111) { + uint8_t mask = (1 << (num_samples & 0b111)) - 1; + buf.mutable_at(num_bytes - 1) &= mask; + } + } else { + if (out.is_none()) { + auto numpy = pybind11::module::import("numpy"); + out = numpy.attr("empty")(num_samples, numpy.attr("bool_")); + } else if (!pybind11::isinstance>(out)) { + throw std::invalid_argument("`out` wasn't `None` or a bool_ numpy array."); + } + auto buf = pybind11::cast>(out); + if (buf.ndim() != 1) { + throw std::invalid_argument("Output buffer wasn't one dimensional."); + } + if ((size_t)buf.shape(0) != num_samples) { + std::stringstream ss; + ss << "Expected output buffer to have size " << num_samples; + ss << " but its size is " << buf.shape(0) << "."; + throw std::invalid_argument(ss.str()); + } + auto stride = buf.strides(0); + void *start_of_data = (void *)buf.mutable_data(); + + generate_biased_samples_bool( + (uint8_t *)start_of_data, + stride, + num_samples, + p, + self.rng); + } + return out; +} + uint8_t pybind11_object_to_pauli_ixyz(const pybind11::object &obj) { if (pybind11::isinstance(obj)) { std::string_view s = pybind11::cast(obj); @@ -95,6 +230,99 @@ pybind11::object peek_pauli_flips(const FrameSimulator &self, const pybind11: return pybind11::cast(std::move(result)); } +pybind11::object pick_output_numpy_array(pybind11::object output_vs, bool bit_packed, bool transpose, size_t shape1, size_t shape2, const char *name) { + auto numpy = pybind11::module::import("numpy"); + auto dtype = bit_packed ? numpy.attr("uint8") : numpy.attr("bool_"); + auto py_bool = pybind11::module::import("builtins").attr("bool"); + if (transpose) { + std::swap(shape1, shape2); + } + if (bit_packed) { + shape2 = (shape2 + 7) >> 3; + } + auto shape = pybind11::make_tuple(shape1, shape2); + if (pybind11::isinstance(output_vs) && pybind11::bool_(false).equal(output_vs)) { + return pybind11::none(); + } else if (pybind11::isinstance(output_vs) && pybind11::bool_(true).equal(output_vs)) { + return numpy.attr("empty")(shape, dtype); + } else if (bit_packed && pybind11::isinstance>(output_vs) && shape.equal(output_vs.attr("shape"))) { + return output_vs; + } else if (!bit_packed && pybind11::isinstance>(output_vs) && shape.equal(output_vs.attr("shape"))) { + return output_vs; + } else { + std::stringstream ss; + ss << name << " wasn't set to False, True, or a numpy array with dtype=" << pybind11::str(dtype) << " and shape=" << shape; + throw std::invalid_argument(ss.str()); + } +} + +template +pybind11::object to_numpy( + const FrameSimulator &self, + bool bit_packed, + bool transpose, + pybind11::object output_xs, + pybind11::object output_zs, + pybind11::object output_measure_flips, + pybind11::object output_detector_flips, + pybind11::object output_observable_flips) { + output_xs = pick_output_numpy_array(output_xs, bit_packed, transpose, self.num_qubits, self.batch_size, "output_xs"); + output_zs = pick_output_numpy_array(output_zs, bit_packed, transpose, self.num_qubits, self.batch_size, "output_zs"); + output_measure_flips = pick_output_numpy_array(output_measure_flips, bit_packed, transpose, self.m_record.stored, self.batch_size, "output_measure_flips"); + output_detector_flips = pick_output_numpy_array(output_detector_flips, bit_packed, transpose, self.det_record.stored, self.batch_size, "output_detector_flips"); + output_observable_flips = pick_output_numpy_array(output_observable_flips, bit_packed, transpose, self.num_observables, self.batch_size, "output_observable_flips"); + + if (!output_xs.is_none()) { + simd_bit_table_to_numpy( + self.x_table, + self.num_qubits, + self.batch_size, + bit_packed, + transpose, + output_xs); + } + if (!output_zs.is_none()) { + simd_bit_table_to_numpy( + self.z_table, + self.num_qubits, + self.batch_size, + bit_packed, + transpose, + output_zs); + } + if (!output_measure_flips.is_none()) { + simd_bit_table_to_numpy( + self.m_record.storage, + self.m_record.stored, + self.batch_size, + bit_packed, + transpose, + output_measure_flips); + } + if (!output_detector_flips.is_none()) { + simd_bit_table_to_numpy( + self.det_record.storage, + self.det_record.stored, + self.batch_size, + bit_packed, + transpose, + output_detector_flips); + } + if (!output_observable_flips.is_none()) { + simd_bit_table_to_numpy( + self.obs_record, + self.num_observables, + self.batch_size, + bit_packed, + transpose, + output_observable_flips); + } + if (output_xs.is_none() + output_zs.is_none() + output_measure_flips.is_none() + output_detector_flips.is_none() + output_observable_flips.is_none() == 5) { + throw std::invalid_argument("No outputs requested! Specify at least one output_*= argument."); + } + return pybind11::make_tuple(output_xs, output_zs, output_measure_flips, output_detector_flips, output_observable_flips); +} + template FrameSimulator create_frame_simulator( size_t batch_size, bool disable_heisenberg_uncertainty, uint32_t num_qubits, const pybind11::object &seed) { @@ -508,6 +736,229 @@ void stim_pybind::pybind_frame_simulator_methods( )DOC") .data()); + c.def( + "to_numpy", + &to_numpy, + pybind11::kw_only(), + pybind11::arg("bit_packed") = false, + pybind11::arg("transpose") = false, + pybind11::arg("output_xs") = false, + pybind11::arg("output_zs") = false, + pybind11::arg("output_measure_flips") = false, + pybind11::arg("output_detector_flips") = false, + pybind11::arg("output_observable_flips") = false, + clean_doc_string(R"DOC( + @signature def to_numpy(self, *, bit_packed: bool = False, transpose: bool = False, output_xs: Union[bool, np.ndarray] = False, output_zs: Union[bool, np.ndarray] = False, output_measure_flips: Union[bool, np.ndarray] = False, output_detector_flips: Union[bool, np.ndarray] = False, output_observable_flips: Union[bool, np.ndarray] = False) -> Optional[Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]]: + Writes the simulator state into numpy arrays. + + Args: + bit_packed: Whether or not the result is bit packed, storing 8 bits per + byte instead of 1 bit per byte. Bit packing always applies to + the second index of the result. Bits are packed in little endian + order (as if by `np.packbits(X, axis=1, order='little')`). + transpose: Defaults to False. When set to False, the second index of the + returned array (the index affected by bit packing) is the shot index + (meaning the first index is the qubit index or measurement index or + etc). When set to True, results are transposed so that the first + index is the shot index. + output_xs: Defaults to False. When set to False, the X flip data is not + generated and the corresponding array in the result tuple is set to + None. When set to True, a new array is allocated to hold the X flip + data and this array is returned via the result tuple. When set to + a numpy array, the results are written into that array (the shape and + dtype of the array must be exactly correct). + output_zs: Defaults to False. When set to False, the Z flip data is not + generated and the corresponding array in the result tuple is set to + None. When set to True, a new array is allocated to hold the Z flip + data and this array is returned via the result tuple. When set to + a numpy array, the results are written into that array (the shape and + dtype of the array must be exactly correct). + output_measure_flips: Defaults to False. When set to False, the measure + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the measure flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + output_detector_flips: Defaults to False. When set to False, the detector + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the detector flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + output_observable_flips: Defaults to False. When set to False, the obs + flip data is not generated and the corresponding array in the result + tuple is set to None. When set to True, a new array is allocated to + hold the obs flip data and this array is returned via the result + tuple. When set to a numpy array, the results are written into that + array (the shape and dtype of the array must be exactly correct). + + Returns: + A tuple (xs, zs, ms, ds, os) of numpy arrays. The xs and zs arrays are + the pauli flip data specified using XZ encoding (00=I, 10=X, 11=Y, 01=Z). + The ms array is the measure flip data, the ds array is the detector flip + data, and the os array is the obs flip data. The arrays default to + `None` when the corresponding `output_*` argument was left False. + + The shape and dtype of the data depends on arguments given to the function. + The following specifies each array's shape and dtype for each case: + + if not transpose and not bit_packed: + xs.shape = (sim.batch_size, sim.num_qubits) + zs.shape = (sim.batch_size, sim.num_qubits) + ms.shape = (sim.batch_size, sim.num_measurements) + ds.shape = (sim.batch_size, sim.num_detectors) + os.shape = (sim.batch_size, sim.num_observables) + xs.dtype = np.bool_ + zs.dtype = np.bool_ + ms.dtype = np.bool_ + ds.dtype = np.bool_ + os.dtype = np.bool_ + elif not transpose and bit_packed: + xs.shape = (sim.batch_size, math.ceil(sim.num_qubits / 8)) + zs.shape = (sim.batch_size, math.ceil(sim.num_qubits / 8)) + ms.shape = (sim.batch_size, math.ceil(sim.num_measurements / 8)) + ds.shape = (sim.batch_size, math.ceil(sim.num_detectors / 8)) + os.shape = (sim.batch_size, math.ceil(sim.num_observables / 8)) + xs.dtype = np.uint8 + zs.dtype = np.uint8 + ms.dtype = np.uint8 + ds.dtype = np.uint8 + os.dtype = np.uint8 + elif transpose and not bit_packed: + xs.shape = (sim.num_qubits, sim.batch_size) + zs.shape = (sim.num_qubits, sim.batch_size) + ms.shape = (sim.num_measurements, sim.batch_size) + ds.shape = (sim.num_detectors, sim.batch_size) + os.shape = (sim.num_observables, sim.batch_size) + xs.dtype = np.bool_ + zs.dtype = np.bool_ + ms.dtype = np.bool_ + ds.dtype = np.bool_ + os.dtype = np.bool_ + elif transpose and bit_packed: + xs.shape = (sim.num_qubits, math.ceil(sim.batch_size / 8)) + zs.shape = (sim.num_qubits, math.ceil(sim.batch_size / 8)) + ms.shape = (sim.num_measurements, math.ceil(sim.batch_size / 8)) + ds.shape = (sim.num_detectors, math.ceil(sim.batch_size / 8)) + os.shape = (sim.num_observables, math.ceil(sim.batch_size / 8)) + xs.dtype = np.uint8 + zs.dtype = np.uint8 + ms.dtype = np.uint8 + ds.dtype = np.uint8 + os.dtype = np.uint8 + + Raises: + ValueError: + All the `output_*` arguments were False, or an `output_*` argument + had a shape or dtype inconsistent with the requested data. + + Examples: + >>> import stim + >>> import numpy as np + >>> sim = stim.FlipSimulator(batch_size=9) + >>> sim.do(stim.Circuit('M(1) 0 1 2')) + + >>> ms_buf = np.empty(shape=(9, 1), dtype=np.uint8) + >>> xs, zs, ms, ds, os = sim.to_numpy( + ... transpose=True, + ... bit_packed=True, + ... output_xs=True, + ... output_measure_flips=ms_buf, + ... ) + >>> assert ms is ms_buf + >>> xs + array([[0], + [0], + [0], + [0], + [0], + [0], + [0], + [0], + [0]], dtype=uint8) + >>> zs + >>> ms + array([[7], + [7], + [7], + [7], + [7], + [7], + [7], + [7], + [7]], dtype=uint8) + >>> ds + >>> os + )DOC") + .data()); + + c.def( + "generate_bernoulli_samples", + &generate_bernoulli_samples, + pybind11::arg("num_samples"), + pybind11::kw_only(), + pybind11::arg("p"), + pybind11::arg("bit_packed") = false, + pybind11::arg("out") = pybind11::none(), + clean_doc_string(R"DOC( + @signature def generate_bernoulli_samples(self, num_samples: int, *, p: float, bit_packed: bool = False, out: Optional[np.ndarray] = None) -> np.ndarray: + Uses the simulator's random number generator to produce biased coin flips. + + This method has best performance when specifying `bit_packed=True` and + when specifying an `out=` parameter pointing to a numpy array that has + contiguous data aligned to a 64 bit boundary. (If `out` isn't specified, + the returned numpy array will have this property.) + + Args: + num_samples: The number of samples to produce. + p: The probability of each sample being True instead of False. + bit_packed: Defaults to False (no bit packing). When True, the result + has type np.uint8 instead of np.bool_ and 8 samples are packed into + each byte as if by np.packbits(bitorder='little'). (The bit order + is relevant when producing a number of samples that isn't a multiple + of 8.) + out: Defaults to None (allocate new). A numpy array to write the samples + into. Must have the correct size and dtype. + + Returns: + A numpy array containing the samples. The shape and dtype depends on + the bit_packed argument: + + if not bit_packed: + shape = (num_samples,) + dtype = np.bool_ + elif not transpose and bit_packed: + shape = (math.ceil(num_samples / 8),) + dtype = np.uint8 + + Raises: + ValueError: + The given `out` argument had a shape or dtype inconsistent with the + requested data. + + Examples: + >>> import stim + >>> sim = stim.FlipSimulator(batch_size=256) + >>> r = sim.generate_bernoulli_samples(1001, p=0.25) + >>> r.dtype + dtype('bool') + >>> r.shape + (1001,) + + >>> r = sim.generate_bernoulli_samples(53, p=0.1, bit_packed=True) + >>> r.dtype + dtype('uint8') + >>> r.shape + (7,) + >>> r[6] & 0b1110_0000 # zero'd padding bits + np.uint8(0) + + >>> r2 = sim.generate_bernoulli_samples(53, p=0.2, bit_packed=True, out=r) + >>> r is r2 # Check request to reuse r worked. + True + )DOC") + .data()); + c.def( "get_measurement_flips", &get_measurement_flips, diff --git a/src/stim/simulators/frame_simulator_pybind_test.py b/src/stim/simulators/frame_simulator_pybind_test.py index 0078a8d8e..bda0bbfad 100644 --- a/src/stim/simulators/frame_simulator_pybind_test.py +++ b/src/stim/simulators/frame_simulator_pybind_test.py @@ -413,3 +413,202 @@ def test_repro_heralded_pauli_channel_1_bug(): result = circuit.compile_sampler().sample(1024) assert np.sum(result[:, 0]) > 0 assert np.sum(result[:, 1]) == 0 + + +def test_to_numpy(): + sim = stim.FlipSimulator(batch_size=50) + sim.do(stim.Circuit.generated( + "surface_code:rotated_memory_x", + distance=5, + rounds=3, + after_clifford_depolarization=0.1, + )) + + xs0, zs0, ms0, ds0, os0 = sim.to_numpy( + output_xs=True, + output_zs=True, + output_measure_flips=True, + output_detector_flips=True, + output_observable_flips=True, + ) + for k in range(50): + np.testing.assert_array_equal(xs0[:, k], sim.peek_pauli_flips()[k].to_numpy()[0]) + np.testing.assert_array_equal(zs0[:, k], sim.peek_pauli_flips()[k].to_numpy()[1]) + np.testing.assert_array_equal(ms0[:, k], sim.get_measurement_flips(instance_index=k)) + np.testing.assert_array_equal(ds0[:, k], sim.get_detector_flips(instance_index=k)) + np.testing.assert_array_equal(os0[:, k], sim.get_observable_flips(instance_index=k)) + + xs, zs, ms, ds, os = sim.to_numpy(output_xs=True) + np.testing.assert_array_equal(xs, xs0) + assert zs is None + assert ms is None + assert ds is None + assert os is None + + xs, zs, ms, ds, os = sim.to_numpy(output_zs=True) + assert xs is None + np.testing.assert_array_equal(zs, zs0) + assert ms is None + assert ds is None + assert os is None + + xs, zs, ms, ds, os = sim.to_numpy(output_measure_flips=True) + assert xs is None + assert zs is None + np.testing.assert_array_equal(ms, ms0) + assert ds is None + assert os is None + + xs, zs, ms, ds, os = sim.to_numpy(output_detector_flips=True) + assert xs is None + assert zs is None + assert ms is None + np.testing.assert_array_equal(ds, ds0) + assert os is None + + xs, zs, ms, ds, os = sim.to_numpy(output_observable_flips=True) + assert xs is None + assert zs is None + assert ms is None + assert ds is None + np.testing.assert_array_equal(os, os0) + + xs1 = np.empty_like(xs0) + zs1 = np.empty_like(zs0) + ms1 = np.empty_like(ms0) + ds1 = np.empty_like(ds0) + os1 = np.empty_like(os0) + xs2, zs2, ms2, ds2, os2 = sim.to_numpy( + output_xs=xs1, + output_zs=zs1, + output_measure_flips=ms1, + output_detector_flips=ds1, + output_observable_flips=os1, + ) + assert xs1 is xs2 + assert zs1 is zs2 + assert ms1 is ms2 + assert ds1 is ds2 + assert os1 is os2 + np.testing.assert_array_equal(xs1, xs0) + np.testing.assert_array_equal(zs1, zs0) + np.testing.assert_array_equal(ms1, ms0) + np.testing.assert_array_equal(ds1, ds0) + np.testing.assert_array_equal(os1, os0) + + xs2, zs2, ms2, ds2, os2 = sim.to_numpy( + transpose=True, + output_xs=True, + output_zs=True, + output_measure_flips=True, + output_detector_flips=True, + output_observable_flips=True, + ) + np.testing.assert_array_equal(xs2, np.transpose(xs0)) + np.testing.assert_array_equal(zs2, np.transpose(zs0)) + np.testing.assert_array_equal(ms2, np.transpose(ms0)) + np.testing.assert_array_equal(ds2, np.transpose(ds0)) + np.testing.assert_array_equal(os2, np.transpose(os0)) + + xs2, zs2, ms2, ds2, os2 = sim.to_numpy( + bit_packed=True, + output_xs=True, + output_zs=True, + output_measure_flips=True, + output_detector_flips=True, + output_observable_flips=True, + ) + np.testing.assert_array_equal(xs2, np.packbits(xs0, axis=1, bitorder='little')) + np.testing.assert_array_equal(zs2, np.packbits(zs0, axis=1, bitorder='little')) + np.testing.assert_array_equal(ms2, np.packbits(ms0, axis=1, bitorder='little')) + np.testing.assert_array_equal(ds2, np.packbits(ds0, axis=1, bitorder='little')) + np.testing.assert_array_equal(os2, np.packbits(os0, axis=1, bitorder='little')) + + xs2, zs2, ms2, ds2, os2 = sim.to_numpy( + transpose=True, + bit_packed=True, + output_xs=True, + output_zs=True, + output_measure_flips=True, + output_detector_flips=True, + output_observable_flips=True, + ) + np.testing.assert_array_equal(xs2, np.packbits(np.transpose(xs0), axis=1, bitorder='little')) + np.testing.assert_array_equal(zs2, np.packbits(np.transpose(zs0), axis=1, bitorder='little')) + np.testing.assert_array_equal(ms2, np.packbits(np.transpose(ms0), axis=1, bitorder='little')) + np.testing.assert_array_equal(ds2, np.packbits(np.transpose(ds0), axis=1, bitorder='little')) + np.testing.assert_array_equal(os2, np.packbits(np.transpose(os0), axis=1, bitorder='little')) + + with pytest.raises(ValueError, match="at least one output"): + sim.to_numpy() + with pytest.raises(ValueError, match="shape="): + sim.to_numpy(output_xs=np.empty(shape=(0, 0), dtype=np.uint64)) + with pytest.raises(ValueError, match="shape="): + sim.to_numpy(output_zs=np.empty(shape=(0, 0), dtype=np.uint64)) + with pytest.raises(ValueError, match="shape="): + sim.to_numpy(output_measure_flips=np.empty(shape=(0, 0), dtype=np.uint64)) + with pytest.raises(ValueError, match="shape="): + sim.to_numpy(output_detector_flips=np.empty(shape=(0, 0), dtype=np.uint64)) + with pytest.raises(ValueError, match="shape="): + sim.to_numpy(output_observable_flips=np.empty(shape=(0, 0), dtype=np.uint64)) + + +def test_generate_bernoulli_samples(): + sim = stim.FlipSimulator(batch_size=10) + + v = sim.generate_bernoulli_samples(1001, p=0, bit_packed=False) + assert v.shape == (1001,) + assert v.dtype == np.bool_ + assert np.sum(v) == 0 + + v2 = sim.generate_bernoulli_samples(1001, p=1, bit_packed=False, out=v) + assert v is v2 + assert v.shape == (1001,) + assert v.dtype == np.bool_ + assert np.sum(v) == 1001 + + v = sim.generate_bernoulli_samples(2**16, p=0.25, bit_packed=False) + assert abs(np.sum(v) - 2**16*0.25) < 2**12 + + v = sim.generate_bernoulli_samples(1001, p=0, bit_packed=True) + assert v.shape == (126,) + assert v.dtype == np.uint8 + assert np.sum(np.unpackbits(v, count=1001, bitorder='little')) == 0 + assert np.sum(np.unpackbits(v, count=1008, bitorder='little')) == 0 + + v2 = sim.generate_bernoulli_samples(1001, p=1, bit_packed=True, out=v) + assert v is v2 + assert v.shape == (126,) + assert v.dtype == np.uint8 + assert np.sum(np.unpackbits(v, count=1001, bitorder='little')) == 1001 + assert np.sum(np.unpackbits(v, count=1008, bitorder='little')) == 1001 + + v = sim.generate_bernoulli_samples(256, p=0, bit_packed=True) + assert np.all(v == 0) + + sim.generate_bernoulli_samples(256 - 101, p=1, bit_packed=True, out=v[1:-11]) + for k in v: + print(k) + assert np.all(v[1:-12] == 0xFF) + assert v[-12] == 7 + assert np.all(v[-11:] == 0) + assert np.all(v[:1] == 0) + + v = sim.generate_bernoulli_samples(2**16, p=0.25, bit_packed=True) + assert abs(np.sum(np.unpackbits(v, count=2**16)) - 2**16*0.25) < 2**12 + + v[:] = 0 + sim.generate_bernoulli_samples(2**16 - 1, p=1, bit_packed=True, out=v) + assert np.all(v[:-1] == 0xFF) + assert v[-1] == 0x7F + + v[:] = 0 + sim.generate_bernoulli_samples(2**15, p=1, bit_packed=True, out=v[::2]) + assert np.all(v[0::2] == 0xFF) + assert np.all(v[1::2] == 0) + + v[:] = 0 + sim.generate_bernoulli_samples(2**15 - 1, p=1, bit_packed=True, out=v[::2]) + assert np.all(v[0::2][:-1] == 0xFF) + assert v[0::2][-1] == 0x7F + assert np.all(v[1::2] == 0) diff --git a/src/stim/simulators/matched_error.pybind.cc b/src/stim/simulators/matched_error.pybind.cc index 884464105..141653fdb 100644 --- a/src/stim/simulators/matched_error.pybind.cc +++ b/src/stim/simulators/matched_error.pybind.cc @@ -310,6 +310,14 @@ pybind11::class_ stim_pybind::pybind_gate_target_with_coor problem in a circuit, instead of having to constantly manually look up the coordinates of a qubit index in order to understand what is happening. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) + >>> t.coords + [1.5, 2.0] )DOC") .data()); } @@ -321,6 +329,12 @@ void stim_pybind::pybind_gate_target_with_coords_methods( &GateTargetWithCoords::gate_target, clean_doc_string(R"DOC( Returns the actual gate target as a `stim.GateTarget`. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) )DOC") .data()); @@ -331,6 +345,12 @@ void stim_pybind::pybind_gate_target_with_coords_methods( Returns the associated coordinate information as a list of floats. If there is no coordinate information, returns an empty list. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.coords + [1.5, 2.0] )DOC") .data()); @@ -349,6 +369,14 @@ void stim_pybind::pybind_gate_target_with_coords_methods( pybind11::arg("coords"), clean_doc_string(R"DOC( Creates a stim.GateTargetWithCoords. + + Examples: + >>> import stim + >>> t = stim.GateTargetWithCoords(0, [1.5, 2.0]) + >>> t.gate_target + stim.GateTarget(0) + >>> t.coords + [1.5, 2.0] )DOC") .data()); c.def("__repr__", &GateTargetWithCoords_repr); @@ -373,6 +401,14 @@ pybind11::class_ stim_pybind::pybind_dem_target_with_coords problem in a circuit, instead of having to constantly manually look up the coordinates of a detector index in order to understand what is happening. + + Examples: + >>> import stim + >>> t = stim.DemTargetWithCoords(stim.DemTarget("D1"), [1.5, 2.0]) + >>> t.dem_target + stim.DemTarget('D1') + >>> t.coords + [1.5, 2.0] )DOC") .data()); } @@ -435,7 +471,6 @@ void stim_pybind::pybind_dem_target_with_coords_methods( [](const ExposedDemTarget &dem_target, const std::vector &coords) -> DemTargetWithCoords { return DemTargetWithCoords{dem_target, coords}; }), - pybind11::kw_only(), pybind11::arg("dem_target"), pybind11::arg("coords"), clean_doc_string(R"DOC( @@ -466,6 +501,18 @@ pybind11::class_ stim_pybind::pybind_flipped_measurement(pyb Gives the measurement's index in the measurement record, and also the observable of the measurement. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement + stim.FlippedMeasurement( + record_index=1, + observable=(stim.GateTargetWithCoords(stim.target_z(10), []),), + ) )DOC") .data()); } @@ -478,6 +525,15 @@ void stim_pybind::pybind_flipped_measurement_methods( The measurement record index of the flipped measurement. For example, the fifth measurement in a circuit has a measurement record index of 4. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement.record_index + 1 )DOC") .data()); @@ -488,6 +544,15 @@ void stim_pybind::pybind_flipped_measurement_methods( Returns the observable of the flipped measurement. For example, an `MX 5` measurement will have the observable X5. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... M(0.25) 1 10 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> err[0].circuit_error_locations[0].flipped_measurement.observable + [stim.GateTargetWithCoords(stim.target_z(10), [])] )DOC") .data()); @@ -561,6 +626,19 @@ void stim_pybind::pybind_circuit_targets_inside_instruction_methods( clean_doc_string(R"DOC( Returns the name of the gate / instruction that was being executed. @signature def gate(self) -> Optional[str]: + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.gate + 'X_ERROR' )DOC") .data()); @@ -570,6 +648,21 @@ void stim_pybind::pybind_circuit_targets_inside_instruction_methods( clean_doc_string(R"DOC( Returns the inclusive start of the range of targets that were executing within the gate / instruction. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.target_range_start + 0 + >>> loc.instruction_targets.target_range_end + 1 )DOC") .data()); @@ -579,6 +672,21 @@ void stim_pybind::pybind_circuit_targets_inside_instruction_methods( clean_doc_string(R"DOC( Returns the exclusive end of the range of targets that were executing within the gate / instruction. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.target_range_start + 0 + >>> loc.instruction_targets.target_range_end + 1 )DOC") .data()); @@ -587,6 +695,19 @@ void stim_pybind::pybind_circuit_targets_inside_instruction_methods( &CircuitTargetsInsideInstruction::args, clean_doc_string(R"DOC( Returns parens arguments of the gate / instruction that was being executed. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.args + [0.25] )DOC") .data()); @@ -597,6 +718,19 @@ void stim_pybind::pybind_circuit_targets_inside_instruction_methods( Returns the subset of targets of the gate/instruction that were being executed. Includes coordinate data with the targets. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 1 + ... X_ERROR(0.25) 0 1 + ... M 0 1 + ... DETECTOR(2, 3) rec[-1] rec[-2] + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0] + >>> loc.instruction_targets.targets_in_range + [stim.GateTargetWithCoords(0, [])] )DOC") .data()); @@ -622,6 +756,16 @@ void stim_pybind::pybind_circuit_targets_inside_instruction_methods( pybind11::arg("targets_in_range"), clean_doc_string(R"DOC( Creates a stim.CircuitTargetsInsideInstruction. + + Examples: + >>> import stim + >>> val = stim.CircuitTargetsInsideInstruction( + ... gate='X_ERROR', + ... args=[0.25], + ... target_range_start=0, + ... target_range_end=1, + ... targets_in_range=[stim.GateTargetWithCoords(0, [])], + ... ) )DOC") .data()); c.def("__repr__", &CircuitTargetsInsideInstruction_repr); @@ -878,6 +1022,28 @@ pybind11::class_ stim_pybind::pybind_explained_error(pybind11::m "ExplainedError", clean_doc_string(R"DOC( Describes the location of an error mechanism from a stim circuit. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0]) + ExplainedError { + dem_error_terms: L0 + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } + } )DOC") .data()); } @@ -903,6 +1069,25 @@ void stim_pybind::pybind_explained_error_methods(pybind11::module &m, pybind11:: Note: if this list is empty, it may be because there was a DEM error decomposed into parts where one of the parts is impossible to make on its own from a single circuit error. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0].circuit_error_locations[0]) + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } )DOC") .data()); @@ -928,6 +1113,28 @@ void stim_pybind::pybind_explained_error_methods(pybind11::module &m, pybind11:: pybind11::arg("circuit_error_locations"), clean_doc_string(R"DOC( Creates a stim.ExplainedError. + + Examples: + >>> import stim + >>> err = stim.Circuit(''' + ... R 0 + ... TICK + ... Y_ERROR(0.125) 0 + ... M 0 + ... OBSERVABLE_INCLUDE(0) rec[-1] + ... ''').shortest_graphlike_error() + >>> print(err[0]) + ExplainedError { + dem_error_terms: L0 + CircuitErrorLocation { + flipped_pauli_product: Y0 + Circuit location stack trace: + (after 1 TICKs) + at instruction #3 (Y_ERROR) in the circuit + at target #1 of the instruction + resolving to Y_ERROR(0.125) 0 + } + } )DOC") .data()); c.def("__repr__", &MatchedError_repr); diff --git a/src/stim/simulators/tableau_simulator.pybind.cc b/src/stim/simulators/tableau_simulator.pybind.cc index 58e3427ff..e8eefbbce 100644 --- a/src/stim/simulators/tableau_simulator.pybind.cc +++ b/src/stim/simulators/tableau_simulator.pybind.cc @@ -1,17 +1,3 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - #include "stim/simulators/tableau_simulator.pybind.h" #include "stim/circuit/circuit_instruction.pybind.h" @@ -20,7 +6,6 @@ #include "stim/simulators/tableau_simulator.h" #include "stim/stabilizers/pauli_string.pybind.h" #include "stim/stabilizers/tableau.h" -#include "stim/util_bot/probability_util.h" #include "stim/util_bot/str_util.h" #include "stim/util_top/circuit_vs_amplitudes.h" #include "stim/util_top/stabilizers_to_tableau.h" @@ -575,6 +560,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z -Y +X )DOC") .data()); @@ -597,6 +594,11 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the noise. p: The chance of the error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.depolarize1(0, 1, 2, p=0.01) )DOC") .data()); @@ -620,6 +622,11 @@ void stim_pybind::pybind_tableau_simulator_methods( zip(targets[::1], targets[1::2]). p: The chance of the error being applied, independently, to each qubit pair. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.depolarize1(0, 1, 4, 5, p=0.01) )DOC") .data()); @@ -642,6 +649,11 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the noise. p: The chance of the X error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x_error(0, 1, 2, p=0.01) )DOC") .data()); @@ -663,6 +675,11 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the noise. p: The chance of the Y error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.y_error(0, 1, 2, p=0.01) )DOC") .data()); @@ -684,6 +701,11 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the noise. p: The chance of the Z error being applied, independently, to each qubit. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.z_error(0, 1, 2, p=0.01) )DOC") .data()); @@ -697,6 +719,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_xz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z -Y +X )DOC") .data()); @@ -711,6 +745,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.c_xyz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y +Z +X )DOC") .data()); @@ -725,6 +771,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.c_zyx(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z +X +Y )DOC") .data()); @@ -739,6 +797,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_xy(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y +X -Z )DOC") .data()); @@ -753,6 +823,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.h_yz(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X +Z +Y )DOC") .data()); @@ -766,6 +848,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.x(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X -Y -Z )DOC") .data()); @@ -779,6 +873,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.y(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X +Y -Z )DOC") .data()); @@ -792,6 +898,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.z(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -X -Y +Z )DOC") .data()); @@ -805,6 +923,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.s(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Y -X +Z )DOC") .data()); @@ -819,6 +949,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.s_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -Y +X +Z )DOC") .data()); @@ -833,6 +975,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_x(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Z -Y )DOC") .data()); @@ -847,6 +1001,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_x_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X -Z +Y )DOC") .data()); @@ -861,6 +1027,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_y(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + -Z +Y +X )DOC") .data()); @@ -875,6 +1053,18 @@ void stim_pybind::pybind_tableau_simulator_methods( Args: *targets: The indices of the qubits to target with the gate. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +X +Y +Z + >>> s.sqrt_y_dag(0, 1, 2) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(3))) + +Z +Y -X )DOC") .data()); @@ -890,6 +1080,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.swap(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +Y +X +X +Z )DOC") .data()); @@ -905,6 +1107,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.iswap(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Y +Z )DOC") .data()); @@ -921,6 +1135,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.iswap_dag(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ -Y +Z )DOC") .data()); @@ -936,6 +1162,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cnot(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X )DOC") .data()); @@ -951,6 +1189,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X )DOC") .data()); @@ -966,6 +1216,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X )DOC") .data()); @@ -981,6 +1243,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X )DOC") .data()); @@ -996,6 +1270,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X )DOC") .data()); @@ -1011,6 +1297,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.cy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X )DOC") .data()); @@ -1026,6 +1324,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.zcy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X )DOC") .data()); @@ -1041,6 +1351,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X )DOC") .data()); @@ -1056,6 +1378,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ )DOC") .data()); @@ -1071,6 +1405,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.xcz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ )DOC") .data()); @@ -1086,6 +1432,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycx(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +Z +X )DOC") .data()); @@ -1101,6 +1459,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycy(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +_ +_ )DOC") .data()); @@ -1116,6 +1486,18 @@ void stim_pybind::pybind_tableau_simulator_methods( *targets: The indices of the qubits to target with the gate. Applies the gate to the first two targets, then the next two targets, and so forth. There must be an even number of targets. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.reset_x(0, 3) + >>> s.reset_y(1) + + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +X +Y +Z +X + >>> s.ycz(0, 1, 2, 3) + >>> print(" ".join(str(s.peek_bloch(k)) for k in range(4))) + +_ +_ +_ +_ )DOC") .data()); @@ -1538,6 +1920,15 @@ void stim_pybind::pybind_tableau_simulator_methods( Returns: The measurement result as a bool. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x(1) + >>> s.measure(0) + False + >>> s.measure(1) + True )DOC") .data()); @@ -1558,6 +1949,13 @@ void stim_pybind::pybind_tableau_simulator_methods( Returns: The measurement results as a list of bools. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.x(1) + >>> s.measure_many(0, 1) + [False, True] )DOC") .data());