Skip to content

Commit

Permalink
Add stim.Circuit.pop
Browse files Browse the repository at this point in the history
Fixes #798
  • Loading branch information
Strilanc committed Sep 21, 2024
1 parent c5830cd commit 0bbd469
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 20 deletions.
41 changes: 41 additions & 0 deletions doc/python_api_reference_vDev.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.Circuit.num_qubits`](#stim.Circuit.num_qubits)
- [`stim.Circuit.num_sweep_bits`](#stim.Circuit.num_sweep_bits)
- [`stim.Circuit.num_ticks`](#stim.Circuit.num_ticks)
- [`stim.Circuit.pop`](#stim.Circuit.pop)
- [`stim.Circuit.reference_sample`](#stim.Circuit.reference_sample)
- [`stim.Circuit.search_for_undetectable_logical_errors`](#stim.Circuit.search_for_undetectable_logical_errors)
- [`stim.Circuit.shortest_error_sat_problem`](#stim.Circuit.shortest_error_sat_problem)
Expand Down Expand Up @@ -2692,6 +2693,46 @@ def num_ticks(
"""
```

<a name="stim.Circuit.pop"></a>
```python
# stim.Circuit.pop

# (in class stim.Circuit)
def pop(
self,
index: int = -1,
) -> Union[stim.CircuitInstruction, stim.CircuitRepeatBlock]:
"""Pops an operation from the end of the circuit, or at the given index.
Args:
index: Defaults to -1 (end of list). The index to pop from.
Returns:
The popped instruction.
Raises:
IndexError: The given index is outside the bounds of the circuit.
Examples:
>>> import stim
>>> c = stim.Circuit('''
... H 0
... S 1
... X 2
... Y 3
... ''')
>>> c.pop()
stim.CircuitInstruction('Y', [stim.GateTarget(3)], [])
>>> c.pop(1)
stim.CircuitInstruction('S', [stim.GateTarget(1)], [])
>>> c
stim.Circuit('''
H 0
X 2
''')
"""
```

<a name="stim.Circuit.reference_sample"></a>
```python
# stim.Circuit.reference_sample
Expand Down
33 changes: 33 additions & 0 deletions doc/stim.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1989,6 +1989,39 @@ class Circuit:
... ''').num_ticks
101
"""
def pop(
self,
index: int = -1,
) -> Union[stim.CircuitInstruction, stim.CircuitRepeatBlock]:
"""Pops an operation from the end of the circuit, or at the given index.
Args:
index: Defaults to -1 (end of list). The index to pop from.
Returns:
The popped instruction.
Raises:
IndexError: The given index is outside the bounds of the circuit.
Examples:
>>> import stim
>>> c = stim.Circuit('''
... H 0
... S 1
... X 2
... Y 3
... ''')
>>> c.pop()
stim.CircuitInstruction('Y', [stim.GateTarget(3)], [])
>>> c.pop(1)
stim.CircuitInstruction('S', [stim.GateTarget(1)], [])
>>> c
stim.Circuit('''
H 0
X 2
''')
"""
def reference_sample(
self,
*,
Expand Down
33 changes: 33 additions & 0 deletions glue/python/src/stim/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1989,6 +1989,39 @@ class Circuit:
... ''').num_ticks
101
"""
def pop(
self,
index: int = -1,
) -> Union[stim.CircuitInstruction, stim.CircuitRepeatBlock]:
"""Pops an operation from the end of the circuit, or at the given index.
Args:
index: Defaults to -1 (end of list). The index to pop from.
Returns:
The popped instruction.
Raises:
IndexError: The given index is outside the bounds of the circuit.
Examples:
>>> import stim
>>> c = stim.Circuit('''
... H 0
... S 1
... X 2
... Y 3
... ''')
>>> c.pop()
stim.CircuitInstruction('Y', [stim.GateTarget(3)], [])
>>> c.pop(1)
stim.CircuitInstruction('S', [stim.GateTarget(1)], [])
>>> c
stim.Circuit('''
H 0
X 2
''')
"""
def reference_sample(
self,
*,
Expand Down
93 changes: 73 additions & 20 deletions src/stim/circuit/circuit.pybind.cc
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,41 @@ std::string py_likeliest_error_sat_problem(const Circuit &self, int quantization
return stim::likeliest_error_sat_problem(dem, quantization, format);
}

pybind11::object circuit_get_item(const Circuit &self, const pybind11::object &index_or_slice) {
pybind11::ssize_t index, step, slice_length;
if (normalize_index_or_slice(index_or_slice, self.operations.size(), &index, &step, &slice_length)) {
return pybind11::cast(self.py_get_slice(index, step, slice_length));
}

auto &op = self.operations[index];
if (op.gate_type == GateType::REPEAT) {
return pybind11::cast(CircuitRepeatBlock{op.repeat_block_rep_count(), op.repeat_block_body(self)});
}
std::vector<GateTarget> targets;
for (const auto &e : op.targets) {
targets.push_back(GateTarget(e));
}
std::vector<double> args;
for (const auto &e : op.args) {
args.push_back(e);
}
return pybind11::cast(PyCircuitInstruction(op.gate_type, targets, args));
}

pybind11::object circuit_pop(Circuit &self, pybind11::ssize_t index) {
if (index < -(pybind11::ssize_t)self.operations.size() || index >= (pybind11::ssize_t)self.operations.size()) {
std::stringstream ss;
ss << "not -len(circuit) < index=" << index << " < len(circuit)=" << self.operations.size();
throw std::out_of_range(ss.str());
}
if (index < 0) {
index += self.operations.size();
}

pybind11::object result = circuit_get_item(self, pybind11::cast(index));
self.operations.erase(self.operations.begin() + (size_t)index);
return result;
}
void circuit_insert(Circuit &self, pybind11::ssize_t &index, pybind11::object &operation) {
if (index < 0) {
index += self.operations.size();
Expand Down Expand Up @@ -1175,6 +1210,43 @@ void stim_pybind::pybind_circuit_methods(pybind11::module &, pybind11::class_<Ci
)DOC")
.data());

c.def(
"pop",
&circuit_pop,
pybind11::arg("index") = -1,
clean_doc_string(R"DOC(
@signature def pop(self, index: int = -1) -> Union[stim.CircuitInstruction, stim.CircuitRepeatBlock]:
Pops an operation from the end of the circuit, or at the given index.
Args:
index: Defaults to -1 (end of circuit). The index to pop from.
Returns:
The popped instruction.
Raises:
IndexError: The given index is outside the bounds of the circuit.
Examples:
>>> import stim
>>> c = stim.Circuit('''
... H 0
... S 1
... X 2
... Y 3
... ''')
>>> c.pop()
stim.CircuitInstruction('Y', [stim.GateTarget(3)], [])
>>> c.pop(1)
stim.CircuitInstruction('S', [stim.GateTarget(1)], [])
>>> c
stim.Circuit('''
H 0
X 2
''')
)DOC")
.data());

c.def(
"append_from_stim_program_text",
[](Circuit &self, const char *text) {
Expand Down Expand Up @@ -1674,26 +1746,7 @@ void stim_pybind::pybind_circuit_methods(pybind11::module &, pybind11::class_<Ci

c.def(
"__getitem__",
[](const Circuit &self, const pybind11::object &index_or_slice) -> pybind11::object {
pybind11::ssize_t index, step, slice_length;
if (normalize_index_or_slice(index_or_slice, self.operations.size(), &index, &step, &slice_length)) {
return pybind11::cast(self.py_get_slice(index, step, slice_length));
}

auto &op = self.operations[index];
if (op.gate_type == GateType::REPEAT) {
return pybind11::cast(CircuitRepeatBlock{op.repeat_block_rep_count(), op.repeat_block_body(self)});
}
std::vector<GateTarget> targets;
for (const auto &e : op.targets) {
targets.push_back(GateTarget(e));
}
std::vector<double> args;
for (const auto &e : op.args) {
args.push_back(e);
}
return pybind11::cast(PyCircuitInstruction(op.gate_type, targets, args));
},
&circuit_get_item,
pybind11::arg("index_or_slice"),
clean_doc_string(R"DOC(
Returns copies of instructions from the circuit.
Expand Down
18 changes: 18 additions & 0 deletions src/stim/circuit/circuit_pybind_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1879,3 +1879,21 @@ def test_insert():
M 2
H 1
""")


def test_pop():
with pytest.raises(IndexError, match='index'):
stim.Circuit().pop()
with pytest.raises(IndexError, match='index'):
stim.Circuit().pop(-1)
with pytest.raises(IndexError, match='index'):
stim.Circuit().pop(0)
c = stim.Circuit("H 0")
with pytest.raises(IndexError, match='index'):
c.pop(1)
with pytest.raises(IndexError, match='index'):
c.pop(-2)
assert c.pop(0) == stim.CircuitInstruction("H", [0])
c = stim.Circuit("H 0\n X 1")
assert c.pop() == stim.CircuitInstruction("X", [1])
assert c.pop() == stim.CircuitInstruction("H", [0])

0 comments on commit 0bbd469

Please sign in to comment.