diff --git a/src/circuit/circuit.pybind.cc b/src/circuit/circuit.pybind.cc index 7e618e7fb..c685b1140 100644 --- a/src/circuit/circuit.pybind.cc +++ b/src/circuit/circuit.pybind.cc @@ -20,7 +20,29 @@ #include "circuit.pybind.h" void pybind_circuit(pybind11::module &m) { - pybind11::class_(m, "Circuit", "A mutable stabilizer circuit.") + pybind11::class_( + m, + "Circuit", + R"DOC( + A mutable stabilizer circuit. + + Examples: + >>> import stim + >>> c = stim.Circuit() + >>> c.append_operation("X", [0]) + >>> c.append_operation("M", [0]) + >>> c.compile_sampler().sample(shots=1) + array([[1]], dtype=uint8) + + >>> stim.Circuit(''' + ... H 0 + ... CNOT 0 1 + ... M 0 1 + ... DETECTOR rec[-1] rec[-2] + ... ''').compile_detector_sampler().sample(shots=1) + array([[0]], dtype=uint8) + + )DOC") .def( pybind11::init([](const char *stim_program_text) { Circuit self; @@ -37,21 +59,21 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim >>> empty = stim.Circuit() - >>> not_empty = stim.Circuit(""" + >>> not_empty = stim.Circuit(''' ... X 0 ... CNOT 0 1 ... M 1 - ... """) + ... ''') )DOC") .def_readonly("num_measurements", &Circuit::num_measurements, R"DOC( The number of measurement bits produced when sampling from the circuit. Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... M 0 ... M 0 1 - ... """) + ... ''') >>> c.num_measurements 3 )DOC") @@ -60,15 +82,15 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... M 0 ... M 0 1 - ... """) + ... ''') >>> c.num_qubits 2 - >>> c.append_from_stim_program_text(""" + >>> c.append_from_stim_program_text(''' ... X 100 - ... """) + ... ''') >>> c.num_qubits 101 )DOC") @@ -82,10 +104,10 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... X 2 ... M 0 1 2 - ... """) + ... ''') >>> s = c.compile_sampler() >>> s.sample(shots=1) array([[0, 0, 1]], dtype=uint8) @@ -100,12 +122,12 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... H 0 ... CNOT 0 1 ... M 0 1 ... DETECTOR rec[-1] rec[-2] - ... """) + ... ''') >>> s = c.compile_detector_sampler() >>> s.sample(shots=1) array([[0]], dtype=uint8) @@ -115,10 +137,10 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... X 0 ... Y 1 2 - ... """) + ... ''') >>> c.clear() >>> print(c) # Circuit [num_qubits=0, num_measurements=0] @@ -128,13 +150,13 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim - >>> c1 = stim.Circuit(""" + >>> c1 = stim.Circuit(''' ... X 0 ... Y 1 2 - ... """) - >>> c2 = stim.Circuit(""" + ... ''') + >>> c2 = stim.Circuit(''' ... M 0 1 2 - ... """) + ... ''') >>> c1 += c2 >>> print(c1) # Circuit [num_qubits=3, num_measurements=3] @@ -157,13 +179,13 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim - >>> c1 = stim.Circuit(""" + >>> c1 = stim.Circuit(''' ... X 0 ... Y 1 2 - ... """) - >>> c2 = stim.Circuit(""" + ... ''') + >>> c2 = stim.Circuit(''' ... M 0 1 2 - ... """) + ... ''') >>> print(c1 + c2) # Circuit [num_qubits=3, num_measurements=3] X 0 @@ -175,10 +197,10 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... X 0 ... Y 1 2 - ... """) + ... ''') >>> c *= 3 >>> print(c) # Circuit [num_qubits=3, num_measurements=0] @@ -194,10 +216,10 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... X 0 ... Y 1 2 - ... """) + ... ''') >>> print(c * 3) # Circuit [num_qubits=3, num_measurements=0] X 0 @@ -214,10 +236,10 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... X 0 ... Y 1 2 - ... """) + ... ''') >>> print(3 * c) # Circuit [num_qubits=3, num_measurements=0] X 0 @@ -266,13 +288,13 @@ void pybind_circuit(pybind11::module &m) { Examples: >>> import stim >>> c = stim.Circuit() - >>> c.append_from_stim_program_text(""" + >>> c.append_from_stim_program_text(''' ... H 0 # comment ... CNOT 0 2 ... ... M 2 ... CNOT rec[-1] 1 - ... """) + ... ''') >>> print(c) # Circuit [num_qubits=3, num_measurements=1] H 0 diff --git a/src/py/compiled_measurement_sampler.pybind.cc b/src/py/compiled_measurement_sampler.pybind.cc index 3c0ddd679..178766bb7 100644 --- a/src/py/compiled_measurement_sampler.pybind.cc +++ b/src/py/compiled_measurement_sampler.pybind.cc @@ -71,10 +71,10 @@ void pybind_compiled_measurement_sampler(pybind11::module &m) { Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... X 0 2 3 ... M 0 1 2 3 - ... """) + ... ''') >>> s = c.compile_sampler() >>> s.sample(shots=1) array([[1, 0, 1, 1]], dtype=uint8) @@ -93,10 +93,10 @@ void pybind_compiled_measurement_sampler(pybind11::module &m) { Examples: >>> import stim - >>> c = stim.Circuit(""" + >>> c = stim.Circuit(''' ... X 0 1 2 3 4 5 6 7 10 ... M 0 1 2 3 4 5 6 7 8 9 10 - ... """) + ... ''') >>> s = c.compile_sampler() >>> s.sample_bit_packed(shots=1) array([[255, 4]], dtype=uint8) diff --git a/src/simulators/tableau_simulator.pybind.cc b/src/simulators/tableau_simulator.pybind.cc index 57d9f312b..ec033fb61 100644 --- a/src/simulators/tableau_simulator.pybind.cc +++ b/src/simulators/tableau_simulator.pybind.cc @@ -63,8 +63,36 @@ void pybind_tableau_simulator(pybind11::module &m) { pybind11::class_( m, "TableauSimulator", - "A quantum stabilizer circuit simulator whose state is an inverse stabilizer tableau." - ) + R"DOC( + A quantum stabilizer circuit simulator whose internal state is an inverse stabilizer tableau. + + Supports interactive usage, where gates and measurements are applied on demand. + + Examples: + >>> import stim + >>> s = stim.TableauSimulator() + >>> s.h(0) + >>> if s.measure(0): + ... s.h(1) + ... s.cnot(1, 2) + >>> s.measure(1) == s.measure(2) + True + + >>> s = stim.TableauSimulator() + >>> s.h(0) + >>> s.cnot(0, 1) + >>> s.current_inverse_tableau() + stim.Tableau.from_conjugated_generators( + xs=[ + stim.PauliString("+ZX"), + stim.PauliString("+_X"), + ], + zs=[ + stim.PauliString("+X_"), + stim.PauliString("+XZ"), + ], + ) + )DOC") .def( "current_inverse_tableau", [](TableauSimulator &self) { @@ -73,6 +101,9 @@ void pybind_tableau_simulator(pybind11::module &m) { R"DOC( Returns a copy of the internal state of the simulator as a stim.Tableau. + Returns: + A stim.Tableau copy of the simulator's state. + Examples: >>> import stim >>> s = stim.TableauSimulator() @@ -122,6 +153,9 @@ void pybind_tableau_simulator(pybind11::module &m) { >>> s.do(stim.Circuit("M 0")) >>> s.current_measurement_record() [False, True, True] + + Returns: + A list of booleans containing the result of every measurement performed by the simulator so far. )DOC") .def( "do", @@ -131,175 +165,329 @@ void pybind_tableau_simulator(pybind11::module &m) { (self.*op.gate->tableau_simulator_function)(op.target_data); } }, + pybind11::arg("circuit"), R"DOC( Applies all the operations in the given stim.Circuit to the simulator's state. Examples: >>> import stim >>> s = stim.TableauSimulator() - >>> s.do(stim.Circuit(""" + >>> s.do(stim.Circuit(''' ... X 0 ... M 0 - ... """)) + ... ''')) >>> s.current_measurement_record() [True] + + Args: + circuit: A stim.Circuit containing operations to apply. )DOC") .def( "h", [](TableauSimulator &self, pybind11::args args) { self.H_XZ(args_to_targets(self, args)); }, - "Applies a Hadamard gate to the simulator's state.") + R"DOC( + Applies a Hadamard gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "h_xy", [](TableauSimulator &self, pybind11::args args) { self.H_XY(args_to_targets(self, args)); }, - "Applies a variant of the Hadamard gate that swaps the X and Y axes to the simulator's state.") + R"DOC( + Applies a variant of the Hadamard gate that swaps the X and Y axes to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "h_yz", [](TableauSimulator &self, pybind11::args args) { self.H_YZ(args_to_targets(self, args)); }, - "Applies a variant of the Hadamard gate that swaps the Y and Z axes to the simulator's state.") + R"DOC( + Applies a variant of the Hadamard gate that swaps the Y and Z axes to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "x", [](TableauSimulator &self, pybind11::args args) { self.X(args_to_targets(self, args)); }, - "Applies a Pauli X gate to the simulator's state.") + R"DOC( + Applies a Pauli X gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "y", [](TableauSimulator &self, pybind11::args args) { self.Y(args_to_targets(self, args)); }, - "Applies a Pauli Y gate to the simulator's state.") + R"DOC( + Applies a Pauli Y gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "z", [](TableauSimulator &self, pybind11::args args) { self.Z(args_to_targets(self, args)); }, - "Applies a Pauli Z gate to the simulator's state.") + R"DOC( + Applies a Pauli Z gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "s", [](TableauSimulator &self, pybind11::args args) { self.SQRT_Z(args_to_targets(self, args)); }, - "Applies a SQRT_Z gate to the simulator's state.") + R"DOC( + Applies a SQRT_Z gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "s_dag", [](TableauSimulator &self, pybind11::args args) { self.SQRT_Z_DAG(args_to_targets(self, args)); }, - "Applies a SQRT_Z_DAG gate to the simulator's state.") + R"DOC( + Applies a SQRT_Z_DAG gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "sqrt_x", [](TableauSimulator &self, pybind11::args args) { self.SQRT_X(args_to_targets(self, args)); }, - "Applies a SQRT_X gate to the simulator's state.") + R"DOC( + Applies a SQRT_X gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "sqrt_x_dag", [](TableauSimulator &self, pybind11::args args) { self.SQRT_X_DAG(args_to_targets(self, args)); }, - "Applies a SQRT_X_DAG gate to the simulator's state.") + R"DOC( + Applies a SQRT_X_DAG gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "sqrt_y", [](TableauSimulator &self, pybind11::args args) { self.SQRT_Y(args_to_targets(self, args)); }, - "Applies a SQRT_Y gate to the simulator's state.") + R"DOC( + Applies a SQRT_Y gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "sqrt_y_dag", [](TableauSimulator &self, pybind11::args args) { self.SQRT_Y_DAG(args_to_targets(self, args)); }, - "Applies a SQRT_Y_DAG gate to the simulator's state.") + R"DOC( + Applies a SQRT_Y_DAG gate to the simulator's state. + + Args: + *targets: The indices of the qubits to target with the gate. + )DOC") .def( "swap", [](TableauSimulator &self, pybind11::args args) { self.SWAP(args_to_target_pairs(self, args)); }, - "Applies a swap gate to the simulator's state.") + R"DOC( + Applies a swap gate to the simulator's state. + + Args: + *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. + )DOC") .def( "iswap", [](TableauSimulator &self, pybind11::args args) { self.ISWAP(args_to_target_pairs(self, args)); }, - "Applies an ISWAP gate to the simulator's state.") + R"DOC( + Applies an ISWAP gate to the simulator's state. + + Args: + *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. + )DOC") .def( "iswap_dag", [](TableauSimulator &self, pybind11::args args) { self.ISWAP_DAG(args_to_target_pairs(self, args)); }, - "Applies an ISWAP_DAG gate to the simulator's state.") + R"DOC( + Applies an ISWAP_DAG gate to the simulator's state. + + Args: + *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. + )DOC") .def( "cnot", [](TableauSimulator &self, pybind11::args args) { self.ZCX(args_to_target_pairs(self, args)); }, - "Applies a controlled X gate to the simulator's state.") + R"DOC( + Applies a controlled X gate to the simulator's state. + + Args: + *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. + )DOC") .def( "cz", [](TableauSimulator &self, pybind11::args args) { self.ZCZ(args_to_target_pairs(self, args)); }, - "Applies a controlled Z gate to the simulator's state.") + R"DOC( + Applies a controlled Z gate to the simulator's state. + + Args: + *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. + )DOC") .def( "cy", [](TableauSimulator &self, pybind11::args args) { self.ZCY(args_to_target_pairs(self, args)); }, - "Applies a controlled Y gate to the simulator's state.") + R"DOC( + Applies a controlled Y gate to the simulator's state. + + Args: + *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. + )DOC") .def( "xcx", [](TableauSimulator &self, pybind11::args args) { self.XCX(args_to_target_pairs(self, args)); }, - "Applies an X-controlled X gate to the simulator's state.") + R"DOC( + Applies an X-controlled X gate to the simulator's state. + + Args: + *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. + )DOC") .def( "xcy", [](TableauSimulator &self, pybind11::args args) { self.XCY(args_to_target_pairs(self, args)); }, - "Applies an X-controlled Y gate to the simulator's state.") + R"DOC( + Applies an X-controlled Y gate to the simulator's state. + + Args: + *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. + )DOC") .def( "xcz", [](TableauSimulator &self, pybind11::args args) { self.XCZ(args_to_target_pairs(self, args)); }, - "Applies an X-controlled Z gate to the simulator's state.") + R"DOC( + Applies an X-controlled Z gate to the simulator's state. + + Args: + *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. + )DOC") .def( "ycx", [](TableauSimulator &self, pybind11::args args) { self.YCX(args_to_target_pairs(self, args)); }, - "Applies a Y-controlled X gate to the simulator's state.") + R"DOC( + Applies a Y-controlled X gate to the simulator's state. + + Args: + *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. + )DOC") .def( "ycy", [](TableauSimulator &self, pybind11::args args) { self.YCY(args_to_target_pairs(self, args)); }, - "Applies a Y-controlled Y gate to the simulator's state.") + R"DOC( + Applies a Y-controlled Y gate to the simulator's state. + + Args: + *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. + )DOC") .def( "ycz", [](TableauSimulator &self, pybind11::args args) { self.YCZ(args_to_target_pairs(self, args)); }, - "Applies a Y-controlled Z gate to the simulator's state.") + R"DOC( + Applies a Y-controlled Z gate to the simulator's state. + + Args: + *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. + )DOC") .def( "reset", [](TableauSimulator &self, pybind11::args args) { self.reset(args_to_targets(self, args)); }, - "Resets a qubit's state to zero (e.g. by swapping it for a zero'd qubit from the environment).") + R"DOC( + Resets qubits to zero (e.g. by swapping them for zero'd qubit from the environment). + + Args: + *targets: The indices of the qubits to reset. + )DOC") .def( "measure", [](TableauSimulator &self, uint32_t target) { self.measure(TempViewableData({target})); return (bool)self.measurement_record.back(); }, + pybind11::arg("target"), R"DOC( Measures a single qubit. @@ -308,6 +496,12 @@ void pybind_tableau_simulator(pybind11::module &m) { create a pitfall where typing `if sim.measure(qubit)` would be a bug. To measure multiple qubits, use `TableauSimulator.measure_many`. + + Args: + target: The index of the qubit to measure. + + Returns: + The measurement result as a bool. )DOC") .def( "measure_many", @@ -317,6 +511,14 @@ void pybind_tableau_simulator(pybind11::module &m) { auto e = self.measurement_record.end(); return std::vector(e - converted_args.targets.size(), e); }, - "Measures multiple qubits.") + R"DOC( + Measures multiple qubits. + + Args: + *targets: The indices of the qubits to measure. + + Returns: + The measurement results as a list of bools. + )DOC") .def(pybind11::init(&create_tableau_simulator)); } diff --git a/src/stabilizers/pauli_string.pybind.cc b/src/stabilizers/pauli_string.pybind.cc index e406996df..9b80adadc 100644 --- a/src/stabilizers/pauli_string.pybind.cc +++ b/src/stabilizers/pauli_string.pybind.cc @@ -110,9 +110,11 @@ void pybind_pauli_string(pybind11::module &m) { Examples: >>> import stim - >>> p = stim.PauliString(5) - >>> print(p) + >>> stim.PauliString("XX") * stim.PauliString("YY") + stim.PauliString("-ZZ") + >>> print(stim.PauliString(5)) +_____ + )DOC") .def_static( "random", @@ -328,6 +330,7 @@ void pybind_pauli_string(pybind11::module &m) { int z = self.zs[u]; return (x ^ z) | (z << 1); }, + pybind11::arg("index"), GET_ITEM_DOC) .def( "__getitem__", @@ -344,9 +347,11 @@ void pybind_pauli_string(pybind11::module &m) { return "_XZY"[self.xs[j] + self.zs[j] * 2]; }); }, + pybind11::arg("slice"), GET_ITEM_DOC) .def( pybind11::init(&PauliString::from_str), + pybind11::arg("text"), R"DOC( Creates a stim.PauliString from a text string. @@ -364,7 +369,7 @@ void pybind_pauli_string(pybind11::module &m) { -___X_ Args: - text: The number of qubits the Pauli string acts on. + text: A text description of the Pauli string's contents, such as "+XXX" or "-_YX". )DOC") .def( pybind11::init(), pybind11::arg("num_qubits"), diff --git a/src/stabilizers/tableau.pybind.cc b/src/stabilizers/tableau.pybind.cc index 5e6fd6316..76f2552dc 100644 --- a/src/stabilizers/tableau.pybind.cc +++ b/src/stabilizers/tableau.pybind.cc @@ -27,6 +27,33 @@ void pybind_tableau(pybind11::module &m) { Represents a Clifford operation by explicitly storing how that operation conjugates a list of Pauli group generators into composite Pauli products. + + Examples: + >>> import stim + >>> stim.Tableau.from_named_gate("H") + stim.Tableau.from_conjugated_generators( + xs=[ + stim.PauliString("+Z"), + ], + zs=[ + stim.PauliString("+X"), + ], + ) + + >>> t = stim.Tableau.random(5) + >>> t_inv = t**-1 + >>> print(t * t_inv) + +-xz-xz-xz-xz-xz- + | ++ ++ ++ ++ ++ + | XZ __ __ __ __ + | __ XZ __ __ __ + | __ __ XZ __ __ + | __ __ __ XZ __ + | __ __ __ __ XZ + + >>> x2z3 = t.x_output(2) * t.z_output(3) + >>> t_inv(x2z3) + stim.PauliString("+__XZ_") )DOC") .def( pybind11::init(), pybind11::arg("num_qubits"),