Skip to content

Commit

Permalink
Add shorthand argument to stim.Circuit.has_flow
Browse files Browse the repository at this point in the history
- Refactor a few C++ methods to `std::string_view`
  • Loading branch information
Strilanc committed Nov 27, 2023
1 parent 7f67b8e commit 83d10e7
Show file tree
Hide file tree
Showing 14 changed files with 460 additions and 107 deletions.
41 changes: 32 additions & 9 deletions doc/python_api_reference_vDev.md
Original file line number Diff line number Diff line change
Expand Up @@ -1889,9 +1889,10 @@ def get_final_qubit_coordinates(
# (in class stim.Circuit)
def has_flow(
self,
shorthand: Optional[str] = None,
*,
start: Optional[stim.PauliString] = None,
end: Optional[stim.PauliString] = None,
start: Union[None, str, stim.PauliString] = None,
end: Union[None, str, stim.PauliString] = None,
measurements: Optional[Iterable[Union[int, stim.GateTarget]]] = None,
unsigned: bool = False,
) -> bool:
Expand All @@ -1904,15 +1905,27 @@ def has_flow(
the CNOT flows implemented by the circuit involve these measurements.
A flow like P -> Q means that the circuit transforms P into Q.
A flow like IDENTITY -> P means that the circuit prepares P.
A flow like P -> IDENTITY means that the circuit measures P.
A flow like IDENTITY -> IDENTITY means that the circuit contains a detector.
Args:
A flow like 1 -> P means that the circuit prepares P.
A flow like P -> 1 means that the circuit measures P.
A flow like 1 -> 1 means that the circuit contains a detector.
Args:
shorthand: Specifies the flow as a short string like "IX -> -YZ xor rec[1]".
The text must contain "->" to separate the input pauli string from the
output pauli string. Each pauli string should be a sequence of
characters from "_IXYZ" (or else just "1" to indicate the empty Pauli
string) optionally prefixed by "+" or "-". Measurements are included
by appending " xor rec[k]" for each measurement index k. Indexing uses
the python convention where non-negative indices index from the start
and negative indices index from the end.
start: The input into the flow at the start of the circuit. Defaults to None
(the identity Pauli string).
(the identity Pauli string). When specified, this should be a
`stim.PauliString`, or a `str` (which will be parsed using
`stim.PauliString.__init__`).
end: The output from the flow at the end of the circuit. Defaults to None
(the identity Pauli string).
(the identity Pauli string). When specified, this should be a
`stim.PauliString`, or a `str` (which will be parsed using
`stim.PauliString.__init__`).
measurements: Defaults to None (empty). The indices of measurements to
include in the flow. This should be a collection of integers and/or
stim.GateTarget instances. Indexing uses the python convention where
Expand All @@ -1936,6 +1949,16 @@ def has_flow(
Examples:
>>> import stim
>>> m = stim.Circuit('M 0')
>>> m.has_flow('Z -> Z')
True
>>> m.has_flow('X -> X')
False
>>> m.has_flow('Z -> I')
False
>>> m.has_flow('Z -> I xor rec[-1]')
True
>>> stim.Circuit('''
... RY 0
... ''').has_flow(
Expand Down
41 changes: 32 additions & 9 deletions doc/stim.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1313,9 +1313,10 @@ class Circuit:
"""
def has_flow(
self,
shorthand: Optional[str] = None,
*,
start: Optional[stim.PauliString] = None,
end: Optional[stim.PauliString] = None,
start: Union[None, str, stim.PauliString] = None,
end: Union[None, str, stim.PauliString] = None,
measurements: Optional[Iterable[Union[int, stim.GateTarget]]] = None,
unsigned: bool = False,
) -> bool:
Expand All @@ -1328,15 +1329,27 @@ class Circuit:
the CNOT flows implemented by the circuit involve these measurements.
A flow like P -> Q means that the circuit transforms P into Q.
A flow like IDENTITY -> P means that the circuit prepares P.
A flow like P -> IDENTITY means that the circuit measures P.
A flow like IDENTITY -> IDENTITY means that the circuit contains a detector.
Args:
A flow like 1 -> P means that the circuit prepares P.
A flow like P -> 1 means that the circuit measures P.
A flow like 1 -> 1 means that the circuit contains a detector.
Args:
shorthand: Specifies the flow as a short string like "IX -> -YZ xor rec[1]".
The text must contain "->" to separate the input pauli string from the
output pauli string. Each pauli string should be a sequence of
characters from "_IXYZ" (or else just "1" to indicate the empty Pauli
string) optionally prefixed by "+" or "-". Measurements are included
by appending " xor rec[k]" for each measurement index k. Indexing uses
the python convention where non-negative indices index from the start
and negative indices index from the end.
start: The input into the flow at the start of the circuit. Defaults to None
(the identity Pauli string).
(the identity Pauli string). When specified, this should be a
`stim.PauliString`, or a `str` (which will be parsed using
`stim.PauliString.__init__`).
end: The output from the flow at the end of the circuit. Defaults to None
(the identity Pauli string).
(the identity Pauli string). When specified, this should be a
`stim.PauliString`, or a `str` (which will be parsed using
`stim.PauliString.__init__`).
measurements: Defaults to None (empty). The indices of measurements to
include in the flow. This should be a collection of integers and/or
stim.GateTarget instances. Indexing uses the python convention where
Expand All @@ -1360,6 +1373,16 @@ class Circuit:
Examples:
>>> import stim
>>> m = stim.Circuit('M 0')
>>> m.has_flow('Z -> Z')
True
>>> m.has_flow('X -> X')
False
>>> m.has_flow('Z -> I')
False
>>> m.has_flow('Z -> I xor rec[-1]')
True
>>> stim.Circuit('''
... RY 0
... ''').has_flow(
Expand Down
2 changes: 1 addition & 1 deletion glue/javascript/pauli_string.js.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ ExposedPauliString::ExposedPauliString(const emscripten::val &arg) : pauli_strin
if (arg.isNumber()) {
pauli_string = PauliString<stim::MAX_BITWORD_WIDTH>(js_val_to_uint32_t(arg));
} else if (arg.isString()) {
pauli_string = PauliString<stim::MAX_BITWORD_WIDTH>::from_str(arg.as<std::string>().data());
pauli_string = PauliString<stim::MAX_BITWORD_WIDTH>::from_str(arg.as<std::string>());
} else {
throw std::invalid_argument("Expected an int or a string. Got " + t);
}
Expand Down
41 changes: 32 additions & 9 deletions glue/python/src/stim/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1313,9 +1313,10 @@ class Circuit:
"""
def has_flow(
self,
shorthand: Optional[str] = None,
*,
start: Optional[stim.PauliString] = None,
end: Optional[stim.PauliString] = None,
start: Union[None, str, stim.PauliString] = None,
end: Union[None, str, stim.PauliString] = None,
measurements: Optional[Iterable[Union[int, stim.GateTarget]]] = None,
unsigned: bool = False,
) -> bool:
Expand All @@ -1328,15 +1329,27 @@ class Circuit:
the CNOT flows implemented by the circuit involve these measurements.
A flow like P -> Q means that the circuit transforms P into Q.
A flow like IDENTITY -> P means that the circuit prepares P.
A flow like P -> IDENTITY means that the circuit measures P.
A flow like IDENTITY -> IDENTITY means that the circuit contains a detector.
Args:
A flow like 1 -> P means that the circuit prepares P.
A flow like P -> 1 means that the circuit measures P.
A flow like 1 -> 1 means that the circuit contains a detector.
Args:
shorthand: Specifies the flow as a short string like "IX -> -YZ xor rec[1]".
The text must contain "->" to separate the input pauli string from the
output pauli string. Each pauli string should be a sequence of
characters from "_IXYZ" (or else just "1" to indicate the empty Pauli
string) optionally prefixed by "+" or "-". Measurements are included
by appending " xor rec[k]" for each measurement index k. Indexing uses
the python convention where non-negative indices index from the start
and negative indices index from the end.
start: The input into the flow at the start of the circuit. Defaults to None
(the identity Pauli string).
(the identity Pauli string). When specified, this should be a
`stim.PauliString`, or a `str` (which will be parsed using
`stim.PauliString.__init__`).
end: The output from the flow at the end of the circuit. Defaults to None
(the identity Pauli string).
(the identity Pauli string). When specified, this should be a
`stim.PauliString`, or a `str` (which will be parsed using
`stim.PauliString.__init__`).
measurements: Defaults to None (empty). The indices of measurements to
include in the flow. This should be a collection of integers and/or
stim.GateTarget instances. Indexing uses the python convention where
Expand All @@ -1360,6 +1373,16 @@ class Circuit:
Examples:
>>> import stim
>>> m = stim.Circuit('M 0')
>>> m.has_flow('Z -> Z')
True
>>> m.has_flow('X -> X')
False
>>> m.has_flow('Z -> I')
False
>>> m.has_flow('Z -> I xor rec[-1]')
True
>>> stim.Circuit('''
... RY 0
... ''').has_flow(
Expand Down
18 changes: 8 additions & 10 deletions src/stim/arg_parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,20 +225,20 @@ bool stim::find_bool_argument(const char *name, int argc, const char **argv) {
throw std::invalid_argument(msg.str());
}

bool parse_int64(const char *data, int64_t *out) {
char c = *data;
if (c == 0) {
bool stim::parse_int64(std::string_view data, int64_t *out) {
if (data.empty()) {
return false;
}
bool negate = false;
if (c == '-') {
if (data.starts_with("-")) {
negate = true;
data++;
c = *data;
data = data.substr(1);
} else if (data.starts_with("+")) {
data = data.substr(1);
}

uint64_t accumulator = 0;
while (c) {
for (char c : data) {
if (!(c >= '0' && c <= '9')) {
return false;
}
Expand All @@ -248,8 +248,6 @@ bool parse_int64(const char *data, int64_t *out) {
return false; // Overflow.
}
accumulator = next;
data++;
c = *data;
}

if (negate && accumulator == (uint64_t)INT64_MAX + uint64_t{1}) {
Expand Down Expand Up @@ -423,7 +421,7 @@ uint64_t stim::parse_exact_uint64_t_from_string(const std::string &text) {
if (end == c + text.size()) {
// strtoull silently accepts spaces and negative signs and overflowing
// values. The only guaranteed way I've found to ensure it actually
// worked is to recreate the string and check that it's the sam.e
// worked is to recreate the string and check that it's the same.
std::stringstream ss;
ss << v;
if (ss.str() == text) {
Expand Down
1 change: 1 addition & 0 deletions src/stim/arg_parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ std::vector<std::string> split(char splitter, const std::string &text);

double parse_exact_double_from_string(const std::string &text);
uint64_t parse_exact_uint64_t_from_string(const std::string &text);
bool parse_int64(std::string_view data, int64_t *out);

} // namespace stim

Expand Down
34 changes: 34 additions & 0 deletions src/stim/arg_parse.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,37 @@ TEST(arg_parse, parse_exact_uint64_t_from_string) {
ASSERT_EQ(parse_exact_uint64_t_from_string("2"), 2);
ASSERT_EQ(parse_exact_uint64_t_from_string("18446744073709551615"), UINT64_MAX);
}

TEST(arg_parse, parse_int64) {
int64_t x = 0;

ASSERT_TRUE(parse_int64("+0", &x));
ASSERT_EQ(x, 0);
ASSERT_TRUE(parse_int64("-0", &x));
ASSERT_EQ(x, 0);
ASSERT_TRUE(parse_int64("0", &x));
ASSERT_EQ(x, 0);
ASSERT_TRUE(parse_int64("1", &x));
ASSERT_EQ(x, 1);
ASSERT_TRUE(parse_int64("-1", &x));
ASSERT_EQ(x, -1);
ASSERT_FALSE(parse_int64("i", &x));
ASSERT_FALSE(parse_int64("1i", &x));
ASSERT_FALSE(parse_int64("i1", &x));
ASSERT_FALSE(parse_int64("1e2", &x));
ASSERT_FALSE(parse_int64("12i1", &x));
ASSERT_FALSE(parse_int64("12 ", &x));
ASSERT_FALSE(parse_int64(" 12", &x));

ASSERT_TRUE(parse_int64("0123", &x));
ASSERT_EQ(x, 123);
ASSERT_TRUE(parse_int64("-0123", &x));
ASSERT_EQ(x, -123);

ASSERT_FALSE(parse_int64("-9223372036854775809", &x));
ASSERT_TRUE(parse_int64("-9223372036854775808", &x));
ASSERT_EQ(x, INT64_MIN);
ASSERT_FALSE(parse_int64("9223372036854775808", &x));
ASSERT_TRUE(parse_int64("9223372036854775807", &x));
ASSERT_EQ(x, INT64_MAX);
}
Loading

0 comments on commit 83d10e7

Please sign in to comment.