From 1cdffaf07ddc0caf1fbae752629111d0b0ff491e Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 5 Jun 2025 21:26:49 +0100 Subject: [PATCH 01/11] matrix: use C++ for constant <-> int --- src/main.hpp | 69 ++++++++++++++++++++++++++++++++++----- src/matrix.cpp | 87 +++++++++++++++++++++++++++++--------------------- 2 files changed, 111 insertions(+), 45 deletions(-) diff --git a/src/main.hpp b/src/main.hpp index 6967c5152..c1cb012df 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -80,22 +80,36 @@ namespace libsemigroups { void init_words(py::module&); template - std::vector - to_ints(std::vector> const& vec) { + using int_or_constant = std:: + variant; + + template + Int to_int(int_or_constant val) { + if (std::holds_alternative(val)) { + return std::get<0>(val); + } else if (std::holds_alternative(val)) { + return static_cast(std::get<1>(val)); + } else if (std::holds_alternative(val)) { + return static_cast(std::get<2>(val)); + } else if (std::holds_alternative(val)) { + return static_cast(std::get<3>(val)); + } else if (std::holds_alternative(val)) { + return static_cast(std::get<4>(val)); + } + } + + template + std::vector to_ints(std::vector> const& vec) { std::vector vec_as_ints; for (auto const& val : vec) { - if (std::holds_alternative(val)) { - vec_as_ints.push_back(std::get<0>(val)); - } else { - vec_as_ints.push_back(static_cast(std::get<1>(val))); - } + vec_as_ints.push_back(to_int(val)); } return vec_as_ints; } template std::vector> - to_ints(std::vector>> const& vec) { + to_ints(std::vector>> const& vec) { std::vector> vec_as_ints; for (auto const& val : vec) { vec_as_ints.push_back(to_ints(val)); @@ -103,6 +117,45 @@ namespace libsemigroups { return vec_as_ints; } + template + int_or_constant from_int(int_or_constant val) { + if (std::holds_alternative(val)) { + if (std::get<0>(val) == static_cast(UNDEFINED)) { + return {UNDEFINED}; + } else if (std::get<0>(val) == static_cast(POSITIVE_INFINITY)) { + return {POSITIVE_INFINITY}; + } else if (std::get<0>(val) == static_cast(NEGATIVE_INFINITY)) { + return {NEGATIVE_INFINITY}; + } else if (std::get<0>(val) == static_cast(LIMIT_MAX)) { + return {LIMIT_MAX}; + } + } + return val; + } + + template + int_or_constant from_int(Int val) { + if (val == static_cast(UNDEFINED)) { + return {UNDEFINED}; + } else if (val == static_cast(POSITIVE_INFINITY)) { + return {POSITIVE_INFINITY}; + } else if (val == static_cast(NEGATIVE_INFINITY)) { + return {NEGATIVE_INFINITY}; + } else if (val == static_cast(LIMIT_MAX)) { + return {LIMIT_MAX}; + } + return {val}; + } + + template + void from_ints(std::vector>& vec) { + for (auto& val : vec) { + if (std::holds_alternative(val)) { + val = from_int(val); + } + } + } + } // namespace libsemigroups #endif // SRC_MAIN_HPP_ diff --git a/src/matrix.cpp b/src/matrix.cpp index 7f6ebf720..eb6ff5838 100644 --- a/src/matrix.cpp +++ b/src/matrix.cpp @@ -168,17 +168,20 @@ above in :any:`MatrixKind`. thing.def("__hash__", &Mat::hash_value); thing.def("__copy__", [](Mat const& x) { return Mat(x); }); thing.def( - "_at", + "__getitem__", [](const Mat& mat, py::tuple xy) { - return mat.at(xy[0].cast(), xy[1].cast()); + return from_int(mat.at(xy[0].cast(), xy[1].cast())); }, py::is_operator()); thing.def( - "_at", + "__getitem__", [](Mat const& thing, size_t i) { try { auto r = thing.row(i); - return std::vector(r.begin(), r.end()); + std::vector> result( + r.begin(), r.end()); + from_ints(result); + return result; } catch (LibsemigroupsException const& e) { // This is done so that "list" works as expected for a // matrix @@ -211,9 +214,10 @@ above in :any:`MatrixKind`. py::is_operator()); thing.def( "__setitem__", - [](Mat& mat, - size_t r, - std::vector const& row) { + [](Mat& mat, + size_t r, + std::vector> const& + row) { auto rv = mat.row(r); if (row.size() != rv.size()) { LIBSEMIGROUPS_EXCEPTION( @@ -222,9 +226,12 @@ above in :any:`MatrixKind`. row.size()); } for (auto item : row) { - matrix::throw_if_bad_entry(mat, item); + matrix::throw_if_bad_entry(mat, to_int(item)); + } + auto dit = rv.begin(); + for (auto it = row.cbegin(); it != row.cend(); ++it, ++dit) { + *dit = to_int(*it); } - std::copy(row.cbegin(), row.cend(), rv.begin()); }, py::is_operator()); thing.def( @@ -358,10 +365,10 @@ above in :any:`MatrixKind`. }); thing.def("transpose", [](Mat& thing) { thing.transpose(); }); thing.def("swap", &Mat::swap); - thing.def("_scalar_zero", - [](Mat const& thing) { return thing.scalar_zero(); }); + thing.def("scalar_zero", + [](Mat const& thing) { return from_int(thing.scalar_zero()); }); thing.def("scalar_one", - [](Mat const& thing) { return thing.scalar_one(); }); + [](Mat const& thing) { return from_int(thing.scalar_one()); }); thing.def("number_of_rows", [](Mat const& thing) { return thing.number_of_rows(); }); thing.def("degree", @@ -385,18 +392,19 @@ above in :any:`MatrixKind`. using scalar_type = typename Mat::scalar_type; auto thing = bind_matrix_common(m); - thing.def(py::init([](std::vector> const& rows) { - return make(rows); - }), - py::arg("rows"), - R"pbdoc( + thing.def( + py::init( + [](std::vector>> const& + rows) { return make(to_ints(rows)); }), + py::arg("rows"), + R"pbdoc( Construct a matrix from rows. :param kind: specifies the underlying semiring. :type kind: MatrixKind :param rows: the rows of the matrix. -:type rows: list[list[int | POSITIVE_INFINITY | NEGATIVE_INFINITY]] +:type rows: list[list[int | PositiveInfinity | NegativeInfinity]] :raise RunTimeError: if *kind* is :py:attr:`MatrixKind.MaxPlusTrunc`, @@ -404,11 +412,11 @@ Construct a matrix from rows. :py:attr:`MatrixKind.NTP`. :raise LibsemigroupsError: - if the entries in *rows* are not of equal length. + if the entries in *rows* are not of equal length. :raise LibsemigroupsError: - if any of the entries of the lists in *rows* do not belong to - the underlying semiring. + if any of the entries of the lists in *rows* do not belong to + the underlying semiring. )pbdoc"); thing.def(py::init()); thing.def("one", [](Mat const& self, size_t n) { return Mat::one(n); }); @@ -424,10 +432,12 @@ Construct a matrix from rows. thing.def(py::init([](size_t threshold, size_t r, size_t c) { return Mat(semiring(threshold), r, c); })); - thing.def( - py::init([](size_t threshold, - std::vector> const& entries) { - return make(semiring(threshold), entries); + thing.def(py::init( + [](size_t threshold, + std::vector>> const& + entries) { + return make(semiring(threshold), + to_ints(entries)); })); thing.def("one", [](Mat const& self, size_t n) { return Mat::one(semiring(matrix::threshold(self)), n); @@ -435,7 +445,7 @@ Construct a matrix from rows. thing.def("one", [](Mat const& self) { return self.one(); }); m.def( - "threshold", + "matrix_threshold", [](Mat const& x) { return matrix::threshold(x); }, py::arg("x"), R"pbdoc( @@ -464,12 +474,13 @@ that is a matrix whose kind is any of: using scalar_type = typename Mat::scalar_type; auto thing = bind_matrix_common(m); - thing.def( - py::init([](size_t threshold, - size_t period, - std::vector> const& entries) { + thing.def(py::init( + [](size_t threshold, + size_t period, + std::vector>> const& + entries) { return make(semiring(threshold, period), - entries); + to_ints(entries)); })); thing.def( py::init([](size_t threshold, size_t period, size_t r, size_t c) { @@ -483,7 +494,7 @@ that is a matrix whose kind is any of: thing.def("one", [](Mat const& self) { return self.one(); }); m.def( - "period", + "matrix_period", [](Mat const& x) { return matrix::period(x); }, py::arg("x"), R"pbdoc( @@ -498,7 +509,7 @@ the ntp matrix *x* using its underlying semiring. :rtype: int )pbdoc"); m.def( - "threshold", + "matrix_threshold", [](Mat const& x) { return matrix::threshold(x); }, py::arg("x"), R"pbdoc( @@ -538,6 +549,7 @@ that is a matrix whose kind is any of: py::arg("x"), R"pbdoc( :sig=(x:Matrix)->int: + Returns the size of the row space of a boolean matrix. This function returns the size of the row space of the boolean matrix *x*. @@ -559,7 +571,7 @@ the size of the row space of the boolean matrix *x*. 7 )pbdoc"); m.def( - "row_basis", + "matrix_row_basis", [](BMat<> const& x) { std::vector> result; for (auto rv : matrix::row_basis(x)) { @@ -569,7 +581,7 @@ the size of the row space of the boolean matrix *x*. }, py::arg("x"), R"pbdoc( -:sig=(x:Matrix)->list[list[int | POSITIVE_INFINITY | NEGATIVE_INFINITY]]: +:sig=(x: Matrix) -> list[list[int | PositiveInfinity | NegativeInfinity]]: Returns a row space basis of a matrix as a list of lists. The matrix *x* which must be one of: @@ -589,10 +601,11 @@ of rows. :returns: A basis for the row space of *x*. :rtype: list[list[int | POSITIVE_INFINITY | NEGATIVE_INFINITY]] )pbdoc"); - m.def("row_basis", [](MaxPlusTruncMat<0, 0, 0, int64_t> const& x) { - std::vector> result; + m.def("matrix_row_basis", [](MaxPlusTruncMat<0, 0, 0, int64_t> const& x) { + std::vector>> result; for (auto rv : matrix::row_basis(x)) { result.emplace_back(rv.begin(), rv.end()); + from_ints(result.back()); } return result; }); From cac632010ac54cb72098a9041d66971abd45691e Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 5 Jun 2025 17:52:35 +0100 Subject: [PATCH 02/11] Fix to_/from_ints --- src/forest.cpp | 60 +++++++++---------- .../presentation/__init__.py | 1 - src/main.hpp | 45 ++++++++++---- src/matrix.cpp | 47 ++++++++------- src/transf.cpp | 8 +-- src/word-graph.cpp | 6 +- 6 files changed, 94 insertions(+), 73 deletions(-) diff --git a/src/forest.cpp b/src/forest.cpp index 77d5d7413..c793f03e2 100644 --- a/src/forest.cpp +++ b/src/forest.cpp @@ -24,6 +24,7 @@ // libsemigroups.... #include // for Forest +#include // for rx::to_vector // pybind11.... #include // for py::operator @@ -69,11 +70,11 @@ Constructs a forest with *n* nodes, that is initialised so that the )pbdoc"); thing.def( py::init( - [](std::vector> const& - parents, - std::vector> const& - labels) { - return make(to_ints(parents), to_ints(labels)); + [](std::vector> const& parents, + std::vector> const& labels) { + using node_type = node_type; + return make(to_ints(parents), + to_ints(labels)); }), py::arg("parents"), py::arg("labels"), @@ -141,8 +142,8 @@ the same state as if it had just be constructed as ``Forest(n)``. )pbdoc"); thing.def( "label", - [](Forest const& self, - Forest::node_type i) -> std::variant { + [](Forest const& self, + node_type i) -> int_or_unsigned_constant { if (self.label(i) != UNDEFINED) { return {self.label(i)}; } @@ -173,15 +174,15 @@ Returns the label of the edge from a node to its parent. thing.def( "labels", [](Forest const& self) - -> std::vector> { - std::vector> result; - for (auto node : self.labels()) { - if (node != UNDEFINED) { - result.emplace_back(node); - } else { - result.emplace_back(UNDEFINED); - } - } + -> std::vector> { + auto result + = self.labels() + | rx::transform( + [](auto val) -> int_or_unsigned_constant { + return {val}; + }) + | rx::to_vector(); + from_ints(result); return result; }, R"pbdoc( @@ -218,12 +219,9 @@ in the forest. thing.def(py::self == py::self, py::arg("that")); thing.def( "parent", - [](Forest const& self, - Forest::node_type i) -> std::variant { - if (self.parent(i) != UNDEFINED) { - return {self.parent(i)}; - } - return {UNDEFINED}; + [](Forest const& self, + node_type i) -> int_or_unsigned_constant { + return from_int(self.parent(i)); }, py::arg("i"), R"pbdoc( @@ -249,15 +247,15 @@ Returns the parent of a node. thing.def( "parents", [](Forest const& self) - -> std::vector> { - std::vector> result; - for (auto node : self.parents()) { - if (node != UNDEFINED) { - result.emplace_back(node); - } else { - result.emplace_back(UNDEFINED); - } - } + -> std::vector> { + auto result + = self.parents() + | rx::transform( + [](auto val) -> int_or_unsigned_constant { + return {val}; + }) + | rx::to_vector(); + from_ints(result); return result; }, R"pbdoc( diff --git a/src/libsemigroups_pybind11/presentation/__init__.py b/src/libsemigroups_pybind11/presentation/__init__.py index 61f79a8b9..2ab15e11d 100644 --- a/src/libsemigroups_pybind11/presentation/__init__.py +++ b/src/libsemigroups_pybind11/presentation/__init__.py @@ -14,7 +14,6 @@ from typing_extensions import Self from _libsemigroups_pybind11 import ( # pylint: disable=no-name-in-module - # TODO(0) balance? InversePresentationString as _InversePresentationString, InversePresentationWord as _InversePresentationWord, PresentationString as _PresentationString, diff --git a/src/main.hpp b/src/main.hpp index c1cb012df..b580c853e 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -19,6 +19,7 @@ #ifndef SRC_MAIN_HPP_ #define SRC_MAIN_HPP_ +#include #include // for variant #include @@ -80,8 +81,17 @@ namespace libsemigroups { void init_words(py::module&); template - using int_or_constant = std:: - variant; + using int_or_unsigned_constant + = std::variant; + + template + using int_or_signed_constant = std:: + variant; + + template + using int_or_constant = std::conditional_t, + int_or_signed_constant, + int_or_unsigned_constant>; template Int to_int(int_or_constant val) { @@ -91,10 +101,13 @@ namespace libsemigroups { return static_cast(std::get<1>(val)); } else if (std::holds_alternative(val)) { return static_cast(std::get<2>(val)); - } else if (std::holds_alternative(val)) { - return static_cast(std::get<3>(val)); } else if (std::holds_alternative(val)) { - return static_cast(std::get<4>(val)); + return static_cast(std::get<3>(val)); + } + if constexpr (std::is_signed_v) { + if (std::holds_alternative(val)) { + return static_cast(std::get<4>(val)); + } } } @@ -102,7 +115,7 @@ namespace libsemigroups { std::vector to_ints(std::vector> const& vec) { std::vector vec_as_ints; for (auto const& val : vec) { - vec_as_ints.push_back(to_int(val)); + vec_as_ints.push_back(to_int(val)); } return vec_as_ints; } @@ -112,20 +125,23 @@ namespace libsemigroups { to_ints(std::vector>> const& vec) { std::vector> vec_as_ints; for (auto const& val : vec) { - vec_as_ints.push_back(to_ints(val)); + vec_as_ints.push_back(to_ints(val)); } return vec_as_ints; } template - int_or_constant from_int(int_or_constant val) { + int_or_constant from_int(int_or_constant const& val) { if (std::holds_alternative(val)) { + if constexpr (std::is_signed_v) { + if (std::get<0>(val) == static_cast(NEGATIVE_INFINITY)) { + return {NEGATIVE_INFINITY}; + } + } if (std::get<0>(val) == static_cast(UNDEFINED)) { return {UNDEFINED}; } else if (std::get<0>(val) == static_cast(POSITIVE_INFINITY)) { return {POSITIVE_INFINITY}; - } else if (std::get<0>(val) == static_cast(NEGATIVE_INFINITY)) { - return {NEGATIVE_INFINITY}; } else if (std::get<0>(val) == static_cast(LIMIT_MAX)) { return {LIMIT_MAX}; } @@ -135,12 +151,15 @@ namespace libsemigroups { template int_or_constant from_int(Int val) { + if constexpr (std::is_signed_v) { + if (val == static_cast(NEGATIVE_INFINITY)) { + return {NEGATIVE_INFINITY}; + } + } if (val == static_cast(UNDEFINED)) { return {UNDEFINED}; } else if (val == static_cast(POSITIVE_INFINITY)) { return {POSITIVE_INFINITY}; - } else if (val == static_cast(NEGATIVE_INFINITY)) { - return {NEGATIVE_INFINITY}; } else if (val == static_cast(LIMIT_MAX)) { return {LIMIT_MAX}; } @@ -151,7 +170,7 @@ namespace libsemigroups { void from_ints(std::vector>& vec) { for (auto& val : vec) { if (std::holds_alternative(val)) { - val = from_int(val); + val = from_int(val); } } } diff --git a/src/matrix.cpp b/src/matrix.cpp index eb6ff5838..9369855bd 100644 --- a/src/matrix.cpp +++ b/src/matrix.cpp @@ -157,6 +157,8 @@ namespace libsemigroups { py_type = "NTPMat"; } + using scalar_type = typename Mat::scalar_type; + py::class_ thing(m, py_type.c_str(), R"pbdoc( @@ -169,7 +171,7 @@ above in :any:`MatrixKind`. thing.def("__copy__", [](Mat const& x) { return Mat(x); }); thing.def( "__getitem__", - [](const Mat& mat, py::tuple xy) { + [](Mat const& mat, py::tuple xy) { return from_int(mat.at(xy[0].cast(), xy[1].cast())); }, py::is_operator()); @@ -177,10 +179,10 @@ above in :any:`MatrixKind`. "__getitem__", [](Mat const& thing, size_t i) { try { - auto r = thing.row(i); - std::vector> result( - r.begin(), r.end()); - from_ints(result); + auto r = thing.row(i); + std::vector> result(r.begin(), + r.end()); + from_ints(result); return result; } catch (LibsemigroupsException const& e) { // This is done so that "list" works as expected for a @@ -191,7 +193,7 @@ above in :any:`MatrixKind`. py::is_operator()); thing.def( "__setitem__", - [](Mat& mat, py::tuple xy, typename Mat::scalar_type val) { + [](Mat& mat, py::tuple xy, scalar_type val) { matrix::throw_if_bad_entry(mat, val); auto r = xy[0].cast(); auto c = xy[1].cast(); @@ -214,10 +216,9 @@ above in :any:`MatrixKind`. py::is_operator()); thing.def( "__setitem__", - [](Mat& mat, - size_t r, - std::vector> const& - row) { + [](Mat& mat, + size_t r, + std::vector> const& row) { auto rv = mat.row(r); if (row.size() != rv.size()) { LIBSEMIGROUPS_EXCEPTION( @@ -225,12 +226,12 @@ above in :any:`MatrixKind`. rv.size(), row.size()); } - for (auto item : row) { - matrix::throw_if_bad_entry(mat, to_int(item)); + for (auto& item : row) { + matrix::throw_if_bad_entry(mat, to_int(item)); } auto dit = rv.begin(); for (auto it = row.cbegin(); it != row.cend(); ++it, ++dit) { - *dit = to_int(*it); + *dit = to_int(*it); } }, py::is_operator()); @@ -390,12 +391,13 @@ above in :any:`MatrixKind`. template auto bind_matrix_no_semiring(py::module& m) { using scalar_type = typename Mat::scalar_type; - auto thing = bind_matrix_common(m); + + auto thing = bind_matrix_common(m); thing.def( py::init( [](std::vector>> const& - rows) { return make(to_ints(rows)); }), + rows) { return make(to_ints(rows)); }), py::arg("rows"), R"pbdoc( Construct a matrix from rows. @@ -427,7 +429,8 @@ Construct a matrix from rows. auto bind_matrix_trunc_semiring(py::module& m) { using semiring_type = typename Mat::semiring_type; using scalar_type = typename Mat::scalar_type; - auto thing = bind_matrix_common(m); + + auto thing = bind_matrix_common(m); thing.def(py::init([](size_t threshold, size_t r, size_t c) { return Mat(semiring(threshold), r, c); @@ -437,7 +440,7 @@ Construct a matrix from rows. std::vector>> const& entries) { return make(semiring(threshold), - to_ints(entries)); + to_ints(entries)); })); thing.def("one", [](Mat const& self, size_t n) { return Mat::one(semiring(matrix::threshold(self)), n); @@ -472,7 +475,8 @@ that is a matrix whose kind is any of: auto bind_ntp_matrix(py::module& m) { using semiring_type = typename Mat::semiring_type; using scalar_type = typename Mat::scalar_type; - auto thing = bind_matrix_common(m); + + auto thing = bind_matrix_common(m); thing.def(py::init( [](size_t threshold, @@ -480,7 +484,7 @@ that is a matrix whose kind is any of: std::vector>> const& entries) { return make(semiring(threshold, period), - to_ints(entries)); + to_ints(entries)); })); thing.def( py::init([](size_t threshold, size_t period, size_t r, size_t c) { @@ -601,11 +605,12 @@ of rows. :returns: A basis for the row space of *x*. :rtype: list[list[int | POSITIVE_INFINITY | NEGATIVE_INFINITY]] )pbdoc"); + m.def("matrix_row_basis", [](MaxPlusTruncMat<0, 0, 0, int64_t> const& x) { - std::vector>> result; + std::vector>> result; for (auto rv : matrix::row_basis(x)) { result.emplace_back(rv.begin(), rv.end()); - from_ints(result.back()); + from_ints(result.back()); } return result; }); diff --git a/src/transf.cpp b/src/transf.cpp index ce9df8bd1..894365c13 100644 --- a/src/transf.cpp +++ b/src/transf.cpp @@ -104,7 +104,7 @@ the image of the point ``i`` under the {0} is ``imgs[i]``. thing.def( "__getitem__", [](PTransfSubclass const& a, - size_t b) -> std::variant { + size_t b) -> int_or_unsigned_constant { auto result = a.at(b); if (result != UNDEFINED) { return {result}; @@ -157,7 +157,7 @@ definition, which is equal to the size of :any:`{0}.images`. [](PTransfSubclass& self) { auto r = rx::iterator_range(self.begin(), self.end()) | rx::transform( - [](auto val) -> std::variant { + [](auto val) -> int_or_unsigned_constant { if (val != UNDEFINED) { return {val}; } @@ -520,8 +520,8 @@ among the points where :math:`f` is defined). thing.def( py::init( - [](std::vector> const& imgs) { - return make(to_ints(imgs)); + [](std::vector> const& imgs) { + return make(to_ints(imgs)); }), py::arg("imgs"), R"pbdoc( diff --git a/src/word-graph.cpp b/src/word-graph.cpp index eb2d56910..9fa46a5d3 100644 --- a/src/word-graph.cpp +++ b/src/word-graph.cpp @@ -112,10 +112,10 @@ out-degree of any node is *n*. There are no edges in the defined word graph. out-degree of the word graph.)pbdoc"); thing.def( - py::init([](size_t num_nodes, + py::init([](size_t num_nodes, std::vector>> const& targets) { - return make(num_nodes, to_ints(targets)); + int_or_unsigned_constant>> const& targets) { + return make(num_nodes, to_ints(targets)); }), py::arg("num_nodes"), py::arg("targets"), From 8a487dc31dd6fe1ff5b2f6c33879bb033304badb Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 5 Jun 2025 18:10:35 +0100 Subject: [PATCH 03/11] Fully utilise to/from_int --- src/cong-common.cpp | 10 ++------- src/froidure-pin.cpp | 18 ++++------------- src/kambites.cpp | 8 ++------ src/main.hpp | 6 ++++++ src/paths.cpp | 16 ++++----------- src/todd-coxeter.cpp | 9 ++------- src/transf.cpp | 1 - src/word-graph.cpp | 48 +++++++++----------------------------------- 8 files changed, 30 insertions(+), 86 deletions(-) diff --git a/src/cong-common.cpp b/src/cong-common.cpp index edae932aa..d2aba5512 100644 --- a/src/cong-common.cpp +++ b/src/cong-common.cpp @@ -52,8 +52,6 @@ namespace libsemigroups { using KnuthBendixWordRewriteFromLeft = KnuthBendix; - using int_or_pos_infty = std::variant; - //////////////////////////////////////////////////////////////////////// // Implementation helpers //////////////////////////////////////////////////////////////////////// @@ -411,12 +409,8 @@ Copy a :any:`{name}` object. doc extra_doc) { thing.def( "number_of_classes", - [](Thing& self) -> std::variant { - auto result = self.number_of_classes(); - if (result != POSITIVE_INFINITY) { - return {result}; - } - return {POSITIVE_INFINITY}; + [](Thing& self) { + return from_int(self.number_of_classes()); }, make_doc(R"pbdoc( :sig=(self: {name}) -> int | PositiveInfinity: diff --git a/src/froidure-pin.cpp b/src/froidure-pin.cpp index 2e1277e61..05bb49df3 100644 --- a/src/froidure-pin.cpp +++ b/src/froidure-pin.cpp @@ -205,13 +205,8 @@ See :any:`add_generator` for a detailed description. thing.def( "current_position", - [](FroidurePin_ const& self, - Element const& x) -> std::variant { - auto result = self.current_position(x); - if (result != UNDEFINED) { - return {result}; - } - return {UNDEFINED}; + [](FroidurePin_ const& self, Element const& x) { + return from_int(self.current_position(x)); }, py::arg("x").noconvert(), R"pbdoc( @@ -717,13 +712,8 @@ elements are sorted, or :any:`UNDEFINED` if *i* is greater than m.def( "froidure_pin_current_position", - [](FroidurePinBase const& fpb, - word_type const& w) -> std::variant { - auto result = froidure_pin::current_position(fpb, w); - if (result != UNDEFINED) { - return {result}; - } - return {UNDEFINED}; + [](FroidurePinBase const& fpb, word_type const& w) { + return from_int(froidure_pin::current_position(fpb, w)); }, py::arg("fpb"), py::arg("w"), diff --git a/src/kambites.cpp b/src/kambites.cpp index 71657d67d..6b69a50b4 100644 --- a/src/kambites.cpp +++ b/src/kambites.cpp @@ -139,12 +139,8 @@ is not at least :math:`4`, then an exception is thrown.)pbdoc"sv; thing.def( "small_overlap_class", - [](Kambites_& self) -> std::variant { - auto result = self.small_overlap_class(); - if (result != POSITIVE_INFINITY) { - return {result}; - } - return {POSITIVE_INFINITY}; + [](Kambites_& self) -> int_or_unsigned_constant { + return from_int(self.small_overlap_class()); }, R"pbdoc( :sig=(self: Kambites) -> int | PositiveInfinity: diff --git a/src/main.hpp b/src/main.hpp index b580c853e..c05ed05f3 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -175,6 +175,12 @@ namespace libsemigroups { } } + template + std::pair, int_or_constant> + from_ints(std::pair const& pair) { + return {from_int(pair.first), from_int(pair.second)}; + } + } // namespace libsemigroups #endif // SRC_MAIN_HPP_ diff --git a/src/paths.cpp b/src/paths.cpp index 6981c498a..9042c3648 100644 --- a/src/paths.cpp +++ b/src/paths.cpp @@ -148,13 +148,9 @@ are no more paths in the range, and ``False`` otherwise. thing1.def( "count", - [](Paths_& p) -> std::variant { + [](Paths_& p) { p.throw_if_source_undefined(); - auto result = p.count(); - if (result != POSITIVE_INFINITY) { - return {result}; - } - return {POSITIVE_INFINITY}; + return from_int(p.count()); }, R"pbdoc( :sig=(self: Paths) -> int | PositiveInfinity: @@ -204,12 +200,8 @@ Get the current path in the range. )pbdoc"); thing1.def( "max", - [](Paths_ const& self) -> std::variant { - auto result = self.max(); - if (result != POSITIVE_INFINITY) { - return {result}; - } - return {POSITIVE_INFINITY}; + [](Paths_ const& self) { + return from_int(self.max()); }, R"pbdoc( :sig=(self: Paths) -> int | PositiveInfinity: diff --git a/src/todd-coxeter.cpp b/src/todd-coxeter.cpp index f50e3a0ec..b5d630db5 100644 --- a/src/todd-coxeter.cpp +++ b/src/todd-coxeter.cpp @@ -241,13 +241,8 @@ semigroup. thing.def( "current_index_of", - [](ToddCoxeter_ const& self, - Word const& w) -> std::variant { - auto result = todd_coxeter::current_index_of(self, w); - if (result != UNDEFINED) { - return {result}; - } - return {UNDEFINED}; + [](ToddCoxeter_ const& self, Word const& w) { + return from_int(todd_coxeter::current_index_of(self, w)); }, py::arg("w"), R"pbdoc( diff --git a/src/transf.cpp b/src/transf.cpp index 894365c13..e871e3f74 100644 --- a/src/transf.cpp +++ b/src/transf.cpp @@ -24,7 +24,6 @@ #include #include #include -#include // libsemigroups_pybind11.... #include "debug.hpp" diff --git a/src/word-graph.cpp b/src/word-graph.cpp index 9fa46a5d3..2b844d575 100644 --- a/src/word-graph.cpp +++ b/src/word-graph.cpp @@ -52,8 +52,6 @@ namespace libsemigroups { using node_type = typename WordGraph_::node_type; using label_type = typename WordGraph_::label_type; - using int_or_undefined = std::variant; - py::class_ thing(m, "WordGraph", R"pbdoc( @@ -201,13 +199,9 @@ the word graph. "targets", [](WordGraph_ const& self, node_type source) { auto result - = (self.targets(source) - | rx::transform([](node_type target) -> int_or_undefined { - if (target == UNDEFINED) { - return {UNDEFINED}; - } - return {target}; - })); + = (self.targets(source) | rx::transform([](node_type target) { + return from_int(target); + })); return py::make_iterator(rx::begin(result), rx::end(result)); }, py::arg("source"), @@ -308,15 +302,9 @@ had just been newly constructed with the same parameters *m* and *n*. "labels_and_targets", [](WordGraph_ const& self, node_type source) { auto r = (self.labels_and_targets(source) - | rx::transform( - [](auto const& label_target) - -> std::pair { - if (std::get<1>(label_target) != UNDEFINED) { - return {std::get<0>(label_target), - {std::get<1>(label_target)}}; - } - return {std::get<0>(label_target), {UNDEFINED}}; - })); + | rx::transform([](auto const& label_target) { + return from_ints(label_target); + })); return py::make_iterator(rx::begin(r), rx::end(r)); }, py::arg("source"), @@ -336,18 +324,8 @@ targets of edges with source *source*. :raises LibsemigroupsError: if *source* is out of bounds.)pbdoc"); thing.def( "next_label_and_target", - [](WordGraph_ const& self, - node_type s, - label_type a) -> std::pair { - std::pair result( - self.next_label_and_target(s, a)); - if (std::get<0>(result.first) == UNDEFINED) { - result.first = UNDEFINED; - } - if (std::get<0>(result.second) == UNDEFINED) { - result.second = UNDEFINED; - } - return result; + [](WordGraph_ const& self, node_type s, label_type a) { + return from_ints(self.next_label_and_target(s, a)); }, py::arg("s"), py::arg("a") = 0, @@ -573,14 +551,8 @@ out_degree())`` , then this function adds an edge from *a* to *b* labelled *a*. :complexity: Constant.)pbdoc"); thing.def( "target", - [](WordGraph_ const& self, - node_type source, - label_type a) -> int_or_undefined { - auto t = self.target(source, a); - if (t == UNDEFINED) { - return {UNDEFINED}; - } - return {t}; + [](WordGraph_ const& self, node_type source, label_type a) { + return from_int(self.target(source, a)); }, py::arg("source"), py::arg("a"), From 40531206efa4aaf83349fddc3479d361773c6fd1 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Thu, 5 Jun 2025 21:29:40 +0100 Subject: [PATCH 04/11] matrix --- src/libsemigroups_pybind11/matrix.py | 100 ++------------------------- 1 file changed, 4 insertions(+), 96 deletions(-) diff --git a/src/libsemigroups_pybind11/matrix.py b/src/libsemigroups_pybind11/matrix.py index 20bb51e82..70d16d696 100644 --- a/src/libsemigroups_pybind11/matrix.py +++ b/src/libsemigroups_pybind11/matrix.py @@ -12,7 +12,6 @@ """ from enum import Enum as _Enum -from typing import Union from _libsemigroups_pybind11 import ( # pylint: disable=no-name-in-module,unused-import BMat as _BMat, @@ -28,9 +27,9 @@ PositiveInfinity as _PositiveInfinity, ProjMaxPlusMat as _ProjMaxPlusMat, matrix_row_space_size as row_space_size, - period, # TODO rename matrix_period - row_basis as _row_basis, # TODO rename matrix_period - threshold, # TODO rename matrix_period + matrix_period as period, + matrix_row_basis as row_basis, + matrix_threshold as threshold, ) @@ -63,97 +62,6 @@ class _MatrixKind(_Enum): } -def _convert_matrix_args(*args): - # TODO remove this, sink into C++ - # Convert POSITIVE_INFINITY and NEGATIVE_INFINITY to integers - if len(args) == 0 or not isinstance(args[-1], list): - return args - return ( - *args[:-1], - [ - [ - (z.to_int() if isinstance(z, (_PositiveInfinity, _NegativeInfinity)) else z) - for z in y - ] - for y in args[-1] - ], - ) - - -def _convert_cxx_entry_to_py( - val: int, -) -> Union[int, _PositiveInfinity, _NegativeInfinity]: - # TODO remove this, sink into C++ - # Convert from integers to _POSITIVE_INFINITY and _NEGATIVE_INFINITY - - if val == _POSITIVE_INFINITY: - return _POSITIVE_INFINITY - if val == _NEGATIVE_INFINITY: - return _NEGATIVE_INFINITY - return val - - -def _convert_cxx_row_to_py( - row: list[int], -) -> list[Union[int, _PositiveInfinity, _NegativeInfinity]]: - # TODO remove this, sink into C++ - for i, val in enumerate(row): - row[i] = _convert_cxx_entry_to_py(val) - return row - - -def _convert_cxx_rows_to_py( - rows: list[int], -) -> list[list[Union[int, _PositiveInfinity, _NegativeInfinity]]]: - # TODO remove this, sink into C++ - for i, val in enumerate(rows): - rows[i] = _convert_cxx_row_to_py(val) - return rows - - -def _at(self, arg): - # pylint: disable=protected-access - if isinstance(arg, tuple) and len(arg) == 2: - return _convert_cxx_entry_to_py(self._at(arg)) - if isinstance(arg, int) and arg >= 0: - return _convert_cxx_row_to_py(self._at(arg)) - raise NotImplementedError - - -def _scalar_zero(self) -> Union[int, _PositiveInfinity, _NegativeInfinity]: - # pylint: disable=protected-access - return _convert_cxx_entry_to_py(self._scalar_zero()) - - -def row_basis(x): - """ - Returns a row space basis of a matrix as a list of lists. The matrix *x* which - must be one of: - - * :any:`MatrixKind.Boolean` - * :any:`MatrixKind.MaxPlusTrunc` - - This function returns a row space basis of the matrix *x* as a list of lists - of rows. - - :param x: the matrix. - :type x: Matrix - - :complexity: - :math:`O(r ^ 2 c)` where :math:`r` is the number of rows in ``x`` - and :math:`c` is the number of columns in ``x``. - - :returns: A basis for the row space of *x*. - :rtype: list[list[int | POSITIVE_INFINITY | NEGATIVE_INFINITY]] - """ - return _convert_cxx_rows_to_py(_row_basis(x)) - - -for _Mat in _MatrixKindToCxxType.values(): - _Mat.__getitem__ = _at - _Mat.scalar_zero = _scalar_zero - - # the underscore prefix stops this from appearing in the doc of the # "matrix" submodule. # TODO could update to use kwargs for threshold and period @@ -163,4 +71,4 @@ def _Matrix(kind: _MatrixKind, *args): # pylint: disable=invalid-name """ if not isinstance(kind, _MatrixKind): raise TypeError("the 1st argument must be a _MatrixKind") - return _MatrixKindToCxxType[kind](*_convert_matrix_args(*args)) + return _MatrixKindToCxxType[kind](*args) From b018f65e118264bed2da414f76822ebf724fca80 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Fri, 6 Jun 2025 12:54:21 +0100 Subject: [PATCH 05/11] Remove comparison of UNDEFINED + ints --- src/action.cpp | 15 ++++--- src/aho-corasick.cpp | 20 ++++++--- src/constants.cpp | 41 ----------------- src/present.cpp | 17 ++++--- src/ukkonen.cpp | 16 ++++--- tests/test_action.py | 99 +++++++++++------------------------------ tests/test_constants.py | 4 +- tests/test_ukkonen.py | 7 +-- 8 files changed, 72 insertions(+), 147 deletions(-) diff --git a/src/action.cpp b/src/action.cpp index 0508cf504..058087e2b 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -229,11 +229,14 @@ Returns an iterator yielding the generators. :rtype: Iterator[Element] )pbdoc"); - thing.def("position", - &Action_::position, - py::arg("pt"), - R"pbdoc( -:sig=(self: Action, pt: Point) -> int: + thing.def( + "position", + [](Action_& self, const_reference_point_type pt) { + return from_int(self.position(pt)); + }, + py::arg("pt"), + R"pbdoc( +:sig=(self: Action, pt: Point) -> int | Undefined: Returns the position of a point in the so far discovered points. @@ -243,7 +246,7 @@ Returns the position of a point in the so far discovered points. :complexity: Constant. :returns: The index of *pt* in ``self`` or :any:`UNDEFINED`. -:rtype: int +:rtype: int | Undefined )pbdoc"); thing.def("empty", &Action_::empty, diff --git a/src/aho-corasick.cpp b/src/aho-corasick.cpp index ee4eef2ef..387b548df 100644 --- a/src/aho-corasick.cpp +++ b/src/aho-corasick.cpp @@ -86,11 +86,18 @@ Copy a :any:`AhoCorasick` object. thing.def("__copy__", [](AhoCorasick const& self) { return AhoCorasick(self); }); - thing.def("child", - &AhoCorasick::child, - py::arg("parent"), - py::arg("letter"), - R"pbdoc( + thing.def( + "child", + [](AhoCorasick const& self, + AhoCorasick::index_type parent, + letter_type letter) { + return from_int(self.child(parent, letter)); + }, + py::arg("parent"), + py::arg("letter"), + R"pbdoc( +:sig=(self: AhoCorasick, parent: int, letter: int) -> int | Undefined: + Return the child of *parent* with edge-label *letter* This function returns the index of the child of the node with index @@ -104,14 +111,13 @@ This function returns the index of the child of the node with index :type letter: int :returns: the index of the child. -:rtype: int +:rtype: int | Undefined :raises LibsemigroupsError: if ``throw_if_node_index_not_active(parent)`` throws. :complexity: Constant. .. seealso:: :any:`throw_if_node_index_not_active`. - )pbdoc"); thing.def("height", diff --git a/src/constants.cpp b/src/constants.cpp index de1088412..46ccba7ab 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -43,47 +43,6 @@ namespace libsemigroups { return true; }, py::is_operator()) - .def( - "__eq__", - [](Undefined const& lhop, size_t rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](size_t lhop, Undefined const& rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](Undefined const& lhop, int rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](int lhop, Undefined const& rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](Undefined const& lhop, uint64_t rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](uint64_t lhop, Undefined const& rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__int__", - [](Undefined const& x) -> size_t { return static_cast(x); }) - .def("__chr__", - [](Undefined const& x) -> char { return static_cast(x); }) .def("__hash__", [](Undefined const& op) -> int { return std::hash{}(static_cast(op)); }); diff --git a/src/present.cpp b/src/present.cpp index f8601ee97..1e58b18cf 100644 --- a/src/present.cpp +++ b/src/present.cpp @@ -820,12 +820,17 @@ If no such word can be found, then a word of length :math:`0` is returned. :rtype: :ref:`Word`. )pbdoc"); - m.def("make_semigroup", - &presentation::make_semigroup, - py::arg("p"), - R"pbdoc( -:sig=(p: Presentation) -> Word: + m.def( + "make_semigroup", + [](Presentation_& p) { + using letter_type = Presentation_::letter_type; + return from_int(presentation::make_semigroup(p)); + }, + py::arg("p"), + R"pbdoc( +:sig=(p: Presentation) -> Letter | Undefined: :only-document-once: + Convert a monoid presentation to a semigroup presentation. This function modifies its argument in-place by replacing the empty word in all @@ -838,7 +843,7 @@ identity, then this generator is returned. :type p: Presentation :returns: The new generator added, if any, and :any:`UNDEFINED` if not. -:rtype: :ref:`Word` +:rtype: :ref:`Letter` | Undefined :raises LibsemigroupsError: if :any:`replace_word` or :any:`add_identity_rules` does. diff --git a/src/ukkonen.cpp b/src/ukkonen.cpp index ae90e88bd..71e943a8e 100644 --- a/src/ukkonen.cpp +++ b/src/ukkonen.cpp @@ -41,11 +41,11 @@ namespace libsemigroups { uk.def( "index", [](Ukkonen const& self, Word const& w) { - return self.index(w.begin(), w.end()); + return from_int(self.index(w.begin(), w.end())); }, py::arg("w"), R"pbdoc( -:sig=(self: Ukkonen, w: str | list[int]) -> int: +:sig=(self: Ukkonen, w: str | list[int]) -> int | Undefined: :only-document-once: Find the index of a word in the suffix tree. @@ -59,7 +59,7 @@ suffix tree represents, then :any:`UNDEFINED` is returned. :type w: str | list[int] :returns: The index of *w*. -:rtype: int +:rtype: int | Undefined :raises LibsemigroupsError: if ``throw_if_contains_unique_letter(w)`` throws. @@ -618,9 +618,13 @@ The children of the current node. R"pbdoc( The index of the first letter in the edge leading to the node. )pbdoc"); - node.def_readwrite("parent", - &Ukkonen::Node::parent, - R"pbdoc( + // TODO should the others here also be def_property_readonly? + node.def_property_readonly( + "parent", + [](Ukkonen::Node const& node) { + return from_int(node.parent); + }, + R"pbdoc( The index of the parent node. )pbdoc"); node.def_readwrite("r", diff --git a/tests/test_action.py b/tests/test_action.py index cc8174ca0..5af5aeff1 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -32,29 +32,17 @@ @pytest.fixture def right_actions(): - seed = row_space_basis( - BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]) - ) + seed = row_space_basis(BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]])) result = [ RightAction( seeds=[seed], - generators=[ - BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) - ], + generators=[BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])], ) ] - result[0].add_generator( - BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) - ) - result[0].add_generator( - BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]]) - ) - result[0].add_generator( - BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]]) - ) - result[0].add_generator( - BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]) - ) + result[0].add_generator(BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) + result[0].add_generator(BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]])) + result[0].add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]])) + result[0].add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]])) result.append( RightAction( @@ -144,29 +132,17 @@ def right_actions(): @pytest.fixture def left_actions(): - seed = col_space_basis( - BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]) - ) + seed = col_space_basis(BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]])) result = [ LeftAction( - generators=[ - BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) - ], + generators=[BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])], seeds=[seed], ) ] - result[-1].add_generator( - BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) - ) - result[-1].add_generator( - BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]]) - ) - result[-1].add_generator( - BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]]) - ) - result[-1].add_generator( - BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]) - ) + result[-1].add_generator(BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) + result[-1].add_generator(BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]])) + result[-1].add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]])) + result[-1].add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]])) result.append( LeftAction( @@ -226,27 +202,13 @@ def test_action_001(right_actions, left_actions): assert len(rows) == 553 rows.init() - rows.add_seed( - row_space_basis( - BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]) - ) - ) + rows.add_seed(row_space_basis(BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]))) - rows.add_generator( - BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) - ) - rows.add_generator( - BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) - ) - rows.add_generator( - BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]]) - ) - rows.add_generator( - BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]]) - ) - rows.add_generator( - BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]) - ) + rows.add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) + rows.add_generator(BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) + rows.add_generator(BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]])) + rows.add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]])) + rows.add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]])) assert len(rows) == 553 @@ -260,9 +222,7 @@ def test_action_pperm(right_actions): right = right_actions[1] right.reserve(70000) assert len(right) == 65536 - assert ( - repr(right) == "" - ) + assert repr(right) == "" assert right[666] == PPerm( [0, 2, 7, 8, 9, 10, 11, 12, 13, 14], [0, 2, 7, 8, 9, 10, 11, 12, 13, 14], @@ -307,9 +267,7 @@ def test_action_pperm(right_actions): def test_action_pperm2(): for n in [199, 299, 69999]: - act = RightAction( - generators=[PPerm(range(0, n - 1), range(1, n), n)], seeds=[[0]] - ) + act = RightAction(generators=[PPerm(range(0, n - 1), range(1, n), n)], seeds=[[0]]) assert len(act) == n + 1 @@ -331,9 +289,7 @@ def test_action_coverage(): Action(generators=None, seeds=None) with pytest.raises(KeyError): - Action( - generators=[PPerm], seeds=[BMat8], func=RightAction, side=side.left - ) + Action(generators=[PPerm], seeds=[BMat8], func=RightAction, side=side.left) right = RightAction( generators=[ @@ -368,9 +324,7 @@ def test_action_coverage(): seeds=[PPerm.one(17)], ) - assert ( - repr(right) == "" - ) + assert repr(right) == "" with pytest.raises(TypeError): right.add_generator(BMat8(0)) @@ -400,6 +354,7 @@ def test_action_current_size(right_actions, left_actions): ) act = right_actions[1] right_actions[1].run_until(lambda: x in act) + assert x in act assert right_actions[1].current_size() == 43749 @@ -453,9 +408,7 @@ def test_action_reserve(right_actions, left_actions): def test_action_root_of_scc(right_actions, left_actions): for action in right_actions + left_actions: - expected = BMat8( - [[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]] - ) + expected = BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]) assert action.root_of_scc(0) == expected assert action.root_of_scc(expected) == expected with pytest.raises(TypeError): @@ -467,6 +420,4 @@ def test_action_word_graph(right_actions, left_actions): for action in right_actions + left_actions: wg = action.word_graph() assert wg.number_of_nodes() == len(action) - assert ( - wg.number_of_edges() == len(action) * action.number_of_generators() - ) + assert wg.number_of_edges() == len(action) * action.number_of_generators() diff --git a/tests/test_constants.py b/tests/test_constants.py index b8fa8c00f..41b79e4fa 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -28,8 +28,8 @@ def test_UNDEFINED(): # pylint: disable=invalid-name assert not UNDEFINED == NEGATIVE_INFINITY assert not UNDEFINED == LIMIT_MAX assert not UNDEFINED == 0 - assert 18446744073709551615 == UNDEFINED - assert UNDEFINED == 18446744073709551615 + assert 18446744073709551615 != UNDEFINED + assert UNDEFINED != 18446744073709551615 assert not UNDEFINED != UNDEFINED assert UNDEFINED != POSITIVE_INFINITY diff --git a/tests/test_ukkonen.py b/tests/test_ukkonen.py index 9fb294477..4ceac6e07 100644 --- a/tests/test_ukkonen.py +++ b/tests/test_ukkonen.py @@ -13,7 +13,6 @@ # pylint: disable=fixme, missing-function-docstring # pylint: disable=missing-class-docstring, invalid-name - import pytest from libsemigroups_pybind11 import ( @@ -93,7 +92,7 @@ def test_000_b(): assert not ukkonen.is_subword(t, [3, 3]) assert not ukkonen.is_subword(t, "ab") - with pytest.raises(RuntimeError): + with pytest.raises(TypeError): ukkonen.is_subword(t, [UNDEFINED]) @@ -234,9 +233,7 @@ def test_004(): assert ukkonen.number_of_pieces(t, [2]) == 1 assert ukkonen.pieces(t, [2]) == [[2]] - ukkonen.add_words( - t, [[0, 1, 2, 8, 4, 5, 6, 7], [0, 1, 2], [8, 4, 5], [5, 6], [5, 6, 7]] - ) + ukkonen.add_words(t, [[0, 1, 2, 8, 4, 5, 6, 7], [0, 1, 2], [8, 4, 5], [5, 6], [5, 6, 7]]) assert t.number_of_distinct_words() == 8 assert t.number_of_words() == 9 From f0c7a1be1c2da662eac7a64fa039c4e22e0e503a Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Fri, 6 Jun 2025 13:13:42 +0100 Subject: [PATCH 06/11] Remove comparison of POSITIVE_INFINITY + ints --- src/constants.cpp | 40 ------------------------------------- src/kambites.cpp | 4 +--- src/knuth-bendix-impl.cpp | 42 +++++++++++++++++++++++---------------- src/stephen.cpp | 32 ++++++++++++++++++----------- src/ukkonen.cpp | 6 +++--- tests/test_constants.py | 4 ++-- 6 files changed, 51 insertions(+), 77 deletions(-) diff --git a/src/constants.cpp b/src/constants.cpp index 46ccba7ab..d754b298e 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -72,46 +72,6 @@ namespace libsemigroups { [](PositiveInfinity const& lhop, NegativeInfinity const& rhop) -> bool { return false; }, py::is_operator()) - .def( - "__eq__", - [](int lhop, PositiveInfinity const& rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](PositiveInfinity const& lhop, int rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](int64_t lhop, PositiveInfinity const& rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](PositiveInfinity const& lhop, int64_t rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](uint64_t lhop, PositiveInfinity const& rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](PositiveInfinity const& lhop, uint64_t rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def("to_int", - [](PositiveInfinity const& x) -> int64_t { - return static_cast(x); - }) .def("__hash__", [](PositiveInfinity const& op) -> int { return std::hash{}(static_cast(op)); }); diff --git a/src/kambites.cpp b/src/kambites.cpp index 6b69a50b4..046b62e1c 100644 --- a/src/kambites.cpp +++ b/src/kambites.cpp @@ -139,9 +139,7 @@ is not at least :math:`4`, then an exception is thrown.)pbdoc"sv; thing.def( "small_overlap_class", - [](Kambites_& self) -> int_or_unsigned_constant { - return from_int(self.small_overlap_class()); - }, + [](Kambites_& self) { return from_int(self.small_overlap_class()); }, R"pbdoc( :sig=(self: Kambites) -> int | PositiveInfinity: diff --git a/src/knuth-bendix-impl.cpp b/src/knuth-bendix-impl.cpp index 10965dbe6..9329c016f 100644 --- a/src/knuth-bendix-impl.cpp +++ b/src/knuth-bendix-impl.cpp @@ -203,11 +203,13 @@ system is already confluent. :rtype: KnuthBendix )pbdoc"); - thing.def("max_overlap", - py::overload_cast<>(&KnuthBendixImpl::max_overlap, - py::const_), - R"pbdoc( -:sig=(self: KnuthBendix) -> int: + thing.def( + "max_overlap", + [](KnuthBendixImpl const& self) { + return from_int(self.max_overlap()); + }, + R"pbdoc( +:sig=(self: KnuthBendix) -> int | PositiveInfinity: Return the maximum length of overlaps to be considered. @@ -215,17 +217,19 @@ This function returns the maximum length of the overlap of two left hand sides of rules that should be considered in :any:`Runner.run`. :return: The maximum overlap length -:rtype: int +:rtype: int | PositiveInfinity .. seealso:: :any:`Runner.run`. )pbdoc"); thing.def( "max_overlap", - py::overload_cast(&KnuthBendixImpl::max_overlap), + [](KnuthBendixImpl& self, int_or_constant val) { + return self.max_overlap(to_int(val)); + }, py::arg("val"), R"pbdoc( -:sig=(self: KnuthBendix) -> KnuthBendix: +:sig=(self: KnuthBendix, val: int | PositiveInfinity) -> KnuthBendix: Set the maximum length of overlaps to be considered. @@ -244,11 +248,13 @@ If this value is less than the longest left hand side of a rule, then .. seealso:: :any:`Runner.run`. )pbdoc"); - thing.def("max_rules", - py::overload_cast<>(&KnuthBendixImpl::max_rules, - py::const_), - R"pbdoc( -:sig=(self: KnuthBendix) -> int: + thing.def( + "max_rules", + [](KnuthBendixImpl const& self) { + return from_int(self.max_rules()); + }, + R"pbdoc( +:sig=(self: KnuthBendix) -> int | PositiveInfinity: Return the maximum number of rules. @@ -258,17 +264,19 @@ or :any:`knuth_bendix.by_overlap_length`, then they will terminate and the system may not be confluent. :return: The maximum number of rules the system should contain. -:rtype: int +:rtype: int | PositiveInfinity .. seealso:: :any:`Runner.run`. )pbdoc"); thing.def( "max_rules", - py::overload_cast(&KnuthBendixImpl::max_rules), + [](KnuthBendixImpl& self, int_or_constant val) { + return self.max_rules(to_int(val)); + }, py::arg("val"), R"pbdoc( -:sig=(self: KnuthBendix) -> KnuthBendix: +:sig=(self: KnuthBendix, val: int | PositiveInfinity) -> KnuthBendix: Set the maximum number of rules. @@ -280,7 +288,7 @@ system may not be confluent. By default this value is :any:`POSITIVE_INFINITY`. :param val: The maximum number of rules. -:type val: int +:type val: int | PositiveInfinity :return: ``self``. :rtype: KnuthBendix diff --git a/src/stephen.cpp b/src/stephen.cpp index 14399060d..def5e7b32 100644 --- a/src/stephen.cpp +++ b/src/stephen.cpp @@ -394,12 +394,16 @@ This function triggers the algorithm implemented in this class (if it hasn't bee Termination of the Stephen algorithm is undecidable in general, and this function may never terminate. )pbdoc"); - m.def("stephen_number_of_left_factors", - &stephen::number_of_left_factors, - py::arg("s"), - py::arg("min") = 0, - py::arg("max") = static_cast(POSITIVE_INFINITY), - R"pbdoc( + m.def( + "stephen_number_of_left_factors", + [](Stephen_& s, size_t min, size_t max) { + return from_int( + stephen::number_of_left_factors(s, min, max)); + }, + py::arg("s"), + py::arg("min") = 0, + py::arg("max") = static_cast(POSITIVE_INFINITY), + R"pbdoc( :sig=(s: Stephen, min: int = 0, max: int | PositiveInfinity = POSITIVE_INFINITY) -> int | PositiveInfinity: :only-document-once: Returns the number of left factors with length in a given range. @@ -433,12 +437,16 @@ in the range *min* to *max*. Termination of the Stephen algorithm is undecidable in general, and this function may never terminate. )pbdoc"); - m.def("stephen_number_of_words_accepted", - &stephen::number_of_words_accepted, - py::arg("s"), - py::arg("min") = 0, - py::arg("max") = static_cast(POSITIVE_INFINITY), - R"pbdoc( + m.def( + "stephen_number_of_words_accepted", + [](Stephen_& s, size_t min, size_t max) { + return from_int( + stephen::number_of_words_accepted(s, min, max)); + }, + py::arg("s"), + py::arg("min") = 0, + py::arg("max") = static_cast(POSITIVE_INFINITY), + R"pbdoc( :sig=(s: Stephen, min: int = 0, max: int | PositiveInfinity = POSITIVE_INFINITY) -> int | PositiveInfinity: :only-document-once: diff --git a/src/ukkonen.cpp b/src/ukkonen.cpp index 71e943a8e..52bafaba0 100644 --- a/src/ukkonen.cpp +++ b/src/ukkonen.cpp @@ -378,12 +378,12 @@ contained in *u*. If no such suffix exists, then an empty word is returned. m.def( "number_of_pieces", [](Ukkonen const& u, Word const& w) { - return ukkonen::number_of_pieces(u, w); + return from_int(ukkonen::number_of_pieces(u, w)); }, py::arg("u"), py::arg("w"), R"pbdoc( -:sig=(u: Ukkonen, w: str | list[int]) -> int: +:sig=(u: Ukkonen, w: str | list[int]) -> int | PositiveInfinity: :only-document-once: Find the number of pieces in a decomposition of a word (if any). @@ -400,7 +400,7 @@ Recall that a *piece* is a word that occurs in two distinct positions :type w: str | list[int] :returns: The number of pieces. -:rtype: int +:rtype: int | PositiveInfinity :raises LibsemigroupsError: if ``u.throw_if_contains_unique_letter(w)`` throws. diff --git a/tests/test_constants.py b/tests/test_constants.py index 41b79e4fa..fbbd3d10e 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -50,8 +50,8 @@ def test_POSITIVE_INFINITY(): # pylint: disable=invalid-name Test the properties of the POSITIVE_INFINITY constant """ assert POSITIVE_INFINITY == POSITIVE_INFINITY - assert POSITIVE_INFINITY == 2147483646 - assert 2147483646 == POSITIVE_INFINITY + assert POSITIVE_INFINITY != 2147483646 + assert 2147483646 != POSITIVE_INFINITY assert not POSITIVE_INFINITY == UNDEFINED assert not POSITIVE_INFINITY == NEGATIVE_INFINITY assert not POSITIVE_INFINITY == LIMIT_MAX From a839424f2a8d9d2351ea2745a5480806888ddee7 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 7 Jun 2025 11:51:50 +0100 Subject: [PATCH 07/11] Remove comparison of NEGATIVE_INFINITY + ints --- src/constants.cpp | 28 ---------------------------- tests/test_constants.py | 4 ++-- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/constants.cpp b/src/constants.cpp index d754b298e..b7798576f 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -96,34 +96,6 @@ namespace libsemigroups { [](NegativeInfinity const& lhop, PositiveInfinity const& rhop) -> bool { return lhop == rhop; }, py::is_operator()) - .def( - "__eq__", - [](int lhop, NegativeInfinity const& rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](NegativeInfinity const& lhop, int rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](int64_t lhop, NegativeInfinity const& rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](NegativeInfinity const& lhop, int64_t rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def("to_int", - [](NegativeInfinity const& x) -> int64_t { - return static_cast(x); - }) .def("__hash__", [](NegativeInfinity const& op) -> int { return std::hash{}(static_cast(op)); }); diff --git a/tests/test_constants.py b/tests/test_constants.py index fbbd3d10e..687d10c68 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -91,8 +91,8 @@ def test_NEGATIVE_INFINITY(): # pylint: disable=invalid-name Test the properties of the NEGATIVE_INFINITY constant """ assert NEGATIVE_INFINITY == NEGATIVE_INFINITY - assert NEGATIVE_INFINITY == -2147483648 - assert -2147483648 == NEGATIVE_INFINITY + assert NEGATIVE_INFINITY != -2147483648 + assert -2147483648 != NEGATIVE_INFINITY assert not NEGATIVE_INFINITY == UNDEFINED assert not NEGATIVE_INFINITY == POSITIVE_INFINITY assert not NEGATIVE_INFINITY == LIMIT_MAX From decd88ef8cb9747f97885f78f5450960e358e920 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 7 Jun 2025 11:55:13 +0100 Subject: [PATCH 08/11] Remove comparison of LIMIT_MAX + ints --- src/constants.cpp | 40 ---------------------------------------- tests/test_constants.py | 9 --------- 2 files changed, 49 deletions(-) diff --git a/src/constants.cpp b/src/constants.cpp index b7798576f..73c2b3bc3 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -110,46 +110,6 @@ namespace libsemigroups { .def("__repr__", [](LimitMax const& val) -> std::string { return "LIMIT_MAX"; }) .def(pybind11::self < LimitMax()) - .def(pybind11::self < int()) - .def(int() < pybind11::self) - .def( - "__eq__", - [](int lhop, LimitMax const& rhop) -> bool { return lhop == rhop; }, - py::is_operator()) - .def( - "__eq__", - [](LimitMax const& lhop, int rhop) -> bool { return lhop == rhop; }, - py::is_operator()) - .def( - "__eq__", - [](int64_t lhop, LimitMax const& rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__eq__", - [](LimitMax const& lhop, int64_t rhop) -> bool { - return lhop == rhop; - }, - py::is_operator()) - .def( - "__sub__", - [](LimitMax const& lhs, int rhs) { return lhs - rhs; }, - py::is_operator()) - .def( - "__rsub__", - [](LimitMax const& rhs, int lhs) { return lhs - rhs; }, - py::is_operator()) - .def( - "__sub__", - [](LimitMax const& lhs, int64_t rhs) { return lhs - rhs; }, - py::is_operator()) - .def( - "__rsub__", - [](LimitMax const& rhs, int64_t lhs) { return lhs - rhs; }, - py::is_operator()) - .def("to_int", - [](LimitMax const& x) -> int { return static_cast(x); }) .def("__hash__", [](LimitMax const& op) -> int { return std::hash{}(static_cast(op)); }); diff --git a/tests/test_constants.py b/tests/test_constants.py index 687d10c68..815cc24cc 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -152,14 +152,5 @@ def test_LIMIT_MAX(): # pylint: disable=invalid-name assert not LIMIT_MAX > LIMIT_MAX assert not LIMIT_MAX < LIMIT_MAX - assert LIMIT_MAX - 1 - assert 1 - LIMIT_MAX - assert LIMIT_MAX > 0 - assert not 0 > LIMIT_MAX - assert 0 < LIMIT_MAX - assert 100 < LIMIT_MAX - assert not 0 > LIMIT_MAX - assert not 100 > LIMIT_MAX - d = {LIMIT_MAX: 0} assert LIMIT_MAX in d From bd098cedaf371376221642c911d97264759fb89b Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 7 Jun 2025 11:55:40 +0100 Subject: [PATCH 09/11] Formatting --- src/libsemigroups_pybind11/action.py | 12 ++- src/libsemigroups_pybind11/congruence.py | 4 +- src/libsemigroups_pybind11/knuth_bendix.py | 4 +- src/libsemigroups_pybind11/sims.py | 16 ++- src/libsemigroups_pybind11/transf.py | 12 ++- tests/test_action.py | 98 +++++++++++++----- tests/test_froidure_pin.py | 59 +++++++---- tests/test_schreier_sims.py | 65 +++++++++--- tests/test_sims.py | 31 ++++-- tests/test_stephen.py | 109 +++++++++++++++------ tests/test_to.py | 32 ++++-- tests/test_ukkonen.py | 4 +- 12 files changed, 334 insertions(+), 112 deletions(-) diff --git a/src/libsemigroups_pybind11/action.py b/src/libsemigroups_pybind11/action.py index 44d81f656..c3947278d 100644 --- a/src/libsemigroups_pybind11/action.py +++ b/src/libsemigroups_pybind11/action.py @@ -175,7 +175,9 @@ class Action(_CxxWrapper): # pylint: disable=missing-class-docstring ######################################################################## # pylint: disable=redefined-outer-name - def __init__(self: _Self, *args, generators=None, seeds=None, func=None, side=None) -> None: + def __init__( + self: _Self, *args, generators=None, seeds=None, func=None, side=None + ) -> None: """ :sig=(self: Action, generators=None, seeds=None, func=None, side=None) -> None: @@ -207,7 +209,9 @@ def __init__(self: _Self, *args, generators=None, seeds=None, func=None, side=No if _to_cxx(self) is not None: return if len(args) != 0: - raise ValueError(f"expected 0 positional arguments, but found {len(args)}") + raise ValueError( + f"expected 0 positional arguments, but found {len(args)}" + ) if not isinstance(generators, list): raise TypeError( "expected the keyword argument 'generators' to be " @@ -270,7 +274,9 @@ def generators(self: _Self) -> Iterator[Element]: _copy_cxx_mem_fns(_RightActionPPerm1PPerm1, Action) -for _type in ( +for ( + _type +) in ( Action._py_template_params_to_cxx_type.values() # pylint: disable=protected-access ): _register_cxx_wrapped_type(_type, Action) diff --git a/src/libsemigroups_pybind11/congruence.py b/src/libsemigroups_pybind11/congruence.py index 21c534b82..5d71e6f94 100644 --- a/src/libsemigroups_pybind11/congruence.py +++ b/src/libsemigroups_pybind11/congruence.py @@ -71,7 +71,9 @@ def __init__(self: _Self, *args, **kwargs) -> None: ) self.init_cxx_obj(*args) - def get(self: _Self, t: type) -> Union[_Kambites, _KnuthBendix, _ToddCoxeter]: + def get( + self: _Self, t: type + ) -> Union[_Kambites, _KnuthBendix, _ToddCoxeter]: """ :sig=(self: Congruence, t: type) -> Kambites | KnuthBendix | ToddCoxeter: Returns the *t* instance used to compute the congruence (if any). diff --git a/src/libsemigroups_pybind11/knuth_bendix.py b/src/libsemigroups_pybind11/knuth_bendix.py index 6a97392c7..113589aaf 100644 --- a/src/libsemigroups_pybind11/knuth_bendix.py +++ b/src/libsemigroups_pybind11/knuth_bendix.py @@ -89,7 +89,9 @@ def __init__(self, *args, Rewriter="RewriteTrie", **kwargs) -> None: return if len(args) == 2: if isinstance(args[1], _Presentation): - self.py_template_params = args[1].py_template_params + (Rewriter,) + self.py_template_params = args[1].py_template_params + ( + Rewriter, + ) else: raise TypeError( f"expected the 2nd argument to be a Presentation, but found {type(args[1])}" diff --git a/src/libsemigroups_pybind11/sims.py b/src/libsemigroups_pybind11/sims.py index a86ccc8f9..a3b28511c 100644 --- a/src/libsemigroups_pybind11/sims.py +++ b/src/libsemigroups_pybind11/sims.py @@ -50,9 +50,13 @@ def __init__(self: _Self, *args, **kwargs) -> None: if _to_cxx(self) is not None: return if len(args) not in (0, 1): - raise TypeError(f"expected 0 or 1 positional arguments but found {len(args)}") + raise TypeError( + f"expected 0 or 1 positional arguments but found {len(args)}" + ) if len(kwargs) != 0: - raise TypeError(f"expected 0 keyword arguments, but found {len(kwargs)}") + raise TypeError( + f"expected 0 keyword arguments, but found {len(kwargs)}" + ) if len(args) == 0: # self.Word = kwargs["Word"] @@ -218,7 +222,9 @@ def __init__(self: _Self, *args, **kwargs) -> None: ######################################################################## -class SimsRefinerFaithful(_CxxWrapper): # pylint: disable=missing-class-docstring +class SimsRefinerFaithful( + _CxxWrapper +): # pylint: disable=missing-class-docstring __doc__ = _SimsRefinerFaithful.__doc__ _py_template_params_to_cxx_type = { @@ -252,7 +258,9 @@ def __init__(self: _Self, *args, **kwargs) -> None: ): self.py_template_params = (list[int],) else: - raise TypeError("expected the 1st argument to be a list[list[int]]") + raise TypeError( + "expected the 1st argument to be a list[list[int]]" + ) self.init_cxx_obj(*args) diff --git a/src/libsemigroups_pybind11/transf.py b/src/libsemigroups_pybind11/transf.py index f694cf551..e1755ead5 100644 --- a/src/libsemigroups_pybind11/transf.py +++ b/src/libsemigroups_pybind11/transf.py @@ -191,7 +191,9 @@ def __repr__(self: Self) -> str: result = str(self) if len(result) < 72: return result - return f"" + return ( + f"" + ) # We retain a separate __repr__ so that we can distinguish the cxx objects # and their python counterparts. @@ -267,7 +269,9 @@ def __repr__(self: Self) -> str: result = str(self) if len(result) < 72: return result - return f"" + return ( + f"" + ) # We retain a separate __str__ so that we can distinguish the cxx objects # and their python counterparts. @@ -349,7 +353,9 @@ def increase_degree_by(self: Self, n: int) -> Self: @staticmethod @_copydoc(_Perm1.one) def one(n: int) -> Self: - result_type = Perm._py_template_params_to_cxx_type[Perm._py_template_params_from_degree(n)] + result_type = Perm._py_template_params_to_cxx_type[ + Perm._py_template_params_from_degree(n) + ] return _to_py(result_type.one(n)) diff --git a/tests/test_action.py b/tests/test_action.py index 5af5aeff1..40c119166 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -32,17 +32,29 @@ @pytest.fixture def right_actions(): - seed = row_space_basis(BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]])) + seed = row_space_basis( + BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]) + ) result = [ RightAction( seeds=[seed], - generators=[BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])], + generators=[ + BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) + ], ) ] - result[0].add_generator(BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) - result[0].add_generator(BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]])) - result[0].add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]])) - result[0].add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]])) + result[0].add_generator( + BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) + ) + result[0].add_generator( + BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]]) + ) + result[0].add_generator( + BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]]) + ) + result[0].add_generator( + BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]) + ) result.append( RightAction( @@ -132,17 +144,29 @@ def right_actions(): @pytest.fixture def left_actions(): - seed = col_space_basis(BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]])) + seed = col_space_basis( + BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]) + ) result = [ LeftAction( - generators=[BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])], + generators=[ + BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) + ], seeds=[seed], ) ] - result[-1].add_generator(BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) - result[-1].add_generator(BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]])) - result[-1].add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]])) - result[-1].add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]])) + result[-1].add_generator( + BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) + ) + result[-1].add_generator( + BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]]) + ) + result[-1].add_generator( + BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]]) + ) + result[-1].add_generator( + BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]) + ) result.append( LeftAction( @@ -202,13 +226,27 @@ def test_action_001(right_actions, left_actions): assert len(rows) == 553 rows.init() - rows.add_seed(row_space_basis(BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]))) + rows.add_seed( + row_space_basis( + BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]) + ) + ) - rows.add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) - rows.add_generator(BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) - rows.add_generator(BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]])) - rows.add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]])) - rows.add_generator(BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]])) + rows.add_generator( + BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) + ) + rows.add_generator( + BMat8([[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) + ) + rows.add_generator( + BMat8([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]]) + ) + rows.add_generator( + BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 1]]) + ) + rows.add_generator( + BMat8([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]) + ) assert len(rows) == 553 @@ -222,7 +260,9 @@ def test_action_pperm(right_actions): right = right_actions[1] right.reserve(70000) assert len(right) == 65536 - assert repr(right) == "" + assert ( + repr(right) == "" + ) assert right[666] == PPerm( [0, 2, 7, 8, 9, 10, 11, 12, 13, 14], [0, 2, 7, 8, 9, 10, 11, 12, 13, 14], @@ -267,7 +307,9 @@ def test_action_pperm(right_actions): def test_action_pperm2(): for n in [199, 299, 69999]: - act = RightAction(generators=[PPerm(range(0, n - 1), range(1, n), n)], seeds=[[0]]) + act = RightAction( + generators=[PPerm(range(0, n - 1), range(1, n), n)], seeds=[[0]] + ) assert len(act) == n + 1 @@ -289,7 +331,9 @@ def test_action_coverage(): Action(generators=None, seeds=None) with pytest.raises(KeyError): - Action(generators=[PPerm], seeds=[BMat8], func=RightAction, side=side.left) + Action( + generators=[PPerm], seeds=[BMat8], func=RightAction, side=side.left + ) right = RightAction( generators=[ @@ -324,7 +368,9 @@ def test_action_coverage(): seeds=[PPerm.one(17)], ) - assert repr(right) == "" + assert ( + repr(right) == "" + ) with pytest.raises(TypeError): right.add_generator(BMat8(0)) @@ -408,7 +454,9 @@ def test_action_reserve(right_actions, left_actions): def test_action_root_of_scc(right_actions, left_actions): for action in right_actions + left_actions: - expected = BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]]) + expected = BMat8( + [[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]] + ) assert action.root_of_scc(0) == expected assert action.root_of_scc(expected) == expected with pytest.raises(TypeError): @@ -420,4 +468,6 @@ def test_action_word_graph(right_actions, left_actions): for action in right_actions + left_actions: wg = action.word_graph() assert wg.number_of_nodes() == len(action) - assert wg.number_of_edges() == len(action) * action.number_of_generators() + assert ( + wg.number_of_edges() == len(action) * action.number_of_generators() + ) diff --git a/tests/test_froidure_pin.py b/tests/test_froidure_pin.py index 670115da5..e807c70aa 100644 --- a/tests/test_froidure_pin.py +++ b/tests/test_froidure_pin.py @@ -87,7 +87,9 @@ def check_mem_compare(S): ReportGuard(False) with pytest.raises(RuntimeError): - froidure_pin.current_position(S, [0, 0, 0, 0, 0, 0, 0, S.number_of_generators(), 1]) + froidure_pin.current_position( + S, [0, 0, 0, 0, 0, 0, 0, S.number_of_generators(), 1] + ) with pytest.raises(RuntimeError): S.position_of_generator(S.number_of_generators()) @@ -99,14 +101,15 @@ def check_mem_compare(S): # self.assertEqual( # [S.position(froidure_pin.factorisation(S, x)) for x in S], list(range(S.size())) # ) - assert [froidure_pin.current_position(S, froidure_pin.factorisation(S, x)) for x in S] == list( - range(S.size()) - ) + assert [ + froidure_pin.current_position(S, froidure_pin.factorisation(S, x)) + for x in S + ] == list(range(S.size())) assert [S.current_position(x) for x in S] == list(range(S.size())) - assert [S.position_of_generator(i) for i in range(S.number_of_generators())] == list( - range(S.number_of_generators()) - ) + assert [ + S.position_of_generator(i) for i in range(S.number_of_generators()) + ] == list(range(S.number_of_generators())) for x in S: assert S.sorted_position(x) == S.to_sorted_position(S.position(x)) @@ -149,10 +152,14 @@ def check_idempotents(S): pass assert all( - S.fast_product(S.position(x), S.position(x)) == S.position(x) for x in S.idempotents() + S.fast_product(S.position(x), S.position(x)) == S.position(x) + for x in S.idempotents() ) - assert sum(1 for x in range(S.size()) if S.is_idempotent(x)) == S.number_of_idempotents() + assert ( + sum(1 for x in range(S.size()) if S.is_idempotent(x)) + == S.number_of_idempotents() + ) def check_cayley_graphs(S): @@ -184,20 +191,30 @@ def check_factor_prod_rels(S): # (minimal_)factorisation + to_element for i, x in enumerate(S): assert froidure_pin.to_element(S, froidure_pin.factorisation(S, x)) == x - assert froidure_pin.to_element(S, froidure_pin.minimal_factorisation(S, i)) == x + assert ( + froidure_pin.to_element(S, froidure_pin.minimal_factorisation(S, i)) + == x + ) # rules, number_of_rules assert len(list(froidure_pin.rules(S))) == S.number_of_rules() for lhs, rhs in froidure_pin.rules(S): - assert froidure_pin.current_position(S, lhs) == froidure_pin.current_position(S, rhs) - assert froidure_pin.factorisation(S, froidure_pin.current_position(S, rhs)) == rhs + assert froidure_pin.current_position( + S, lhs + ) == froidure_pin.current_position(S, rhs) + assert ( + froidure_pin.factorisation(S, froidure_pin.current_position(S, rhs)) + == rhs + ) # product_by_reduction + fast_product try: for i in range(S.size()): for j in range(S.size()): - assert froidure_pin.product_by_reduction(S, i, j) == S.position(S[i] * S[j]) + assert froidure_pin.product_by_reduction(S, i, j) == S.position( + S[i] * S[j] + ) assert S.fast_product(i, j) == S.position(S[i] * S[j]) except TypeError: # no product defined pass @@ -460,7 +477,9 @@ def test_froidure_pin_min_plus(checks_for_froidure_pin, checks_for_generators): check(FroidurePin(gens)) -def test_froidure_pin_proj_max_plus(checks_for_froidure_pin, checks_for_generators): +def test_froidure_pin_proj_max_plus( + checks_for_froidure_pin, checks_for_generators +): ReportGuard(False) x = Matrix(MatrixKind.ProjMaxPlus, 2, 2) gens = [Matrix(MatrixKind.ProjMaxPlus, [[1, 0], [0, x.scalar_zero()]])] @@ -473,7 +492,9 @@ def test_froidure_pin_proj_max_plus(checks_for_froidure_pin, checks_for_generato check(FroidurePin(gens)) -def test_froidure_pin_max_plus_trunc(checks_for_froidure_pin, checks_for_generators): +def test_froidure_pin_max_plus_trunc( + checks_for_froidure_pin, checks_for_generators +): ReportGuard(False) gens = [Matrix(MatrixKind.MaxPlusTrunc, 11, [[1, 0], [0, 1]])] assert FroidurePin(gens).size() == 12 @@ -485,7 +506,9 @@ def test_froidure_pin_max_plus_trunc(checks_for_froidure_pin, checks_for_generat check(FroidurePin(gens)) -def test_froidure_pin_min_plus_trunc(checks_for_froidure_pin, checks_for_generators): +def test_froidure_pin_min_plus_trunc( + checks_for_froidure_pin, checks_for_generators +): ReportGuard(False) gens = [Matrix(MatrixKind.MinPlusTrunc, 11, [[1, 0], [0, 1]])] assert FroidurePin(gens).size() == 2 @@ -534,7 +557,9 @@ def test_froidure_pin_method_wrap(): S.init() with pytest.raises(LibsemigroupsError): - S.add_generators([Perm([0, 1, 2, 3, 4, 5]), Perm([0, 1, 2, 3, 4, 5, 6])]) + S.add_generators( + [Perm([0, 1, 2, 3, 4, 5]), Perm([0, 1, 2, 3, 4, 5, 6])] + ) S = FroidurePin(Perm([1, 0, 2, 3, 4, 5, 6]), Perm([1, 2, 3, 4, 5, 6, 0])) diff --git a/tests/test_schreier_sims.py b/tests/test_schreier_sims.py index f6b8cabda..57fcfde1c 100644 --- a/tests/test_schreier_sims.py +++ b/tests/test_schreier_sims.py @@ -381,27 +381,45 @@ def check_SchreierSims_001(n): S.init() assert S.size() == 1 S.add_generator( - Perm([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + list(range(17, n))) + Perm( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + + list(range(17, n)) + ) ) S.add_generator( - Perm([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 14] + list(range(17, n))) + Perm( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 14] + + list(range(17, n)) + ) ) assert not S.currently_contains( - Perm([1, 0, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + list(range(17, n))) + Perm( + [1, 0, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + list(range(17, n)) + ) ) assert S.current_size() == 17 assert S.size() == 177843714048000 assert S.base(0) == 0 assert S.contains( - Perm([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + list(range(17, n))) + Perm( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + + list(range(17, n)) + ) ) assert not S.contains( - Perm([1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + list(range(17, n))) + Perm( + [1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + list(range(17, n)) + ) ) assert S.contains( - Perm([1, 0, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + list(range(17, n))) + Perm( + [1, 0, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + list(range(17, n)) + ) ) S.init() @@ -412,12 +430,18 @@ def check_SchreierSims_001(n): S.add_base_point(14) S.add_base_point(15) S.add_generator( - Perm([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + list(range(17, n))) + Perm( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + + list(range(17, n)) + ) ) S.add_base_point(1) S.add_base_point(3) S.add_generator( - Perm([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 14] + list(range(17, n))) + Perm( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 14] + + list(range(17, n)) + ) ) assert S.base_size() == 4 assert S.size() == 177843714048000 @@ -433,24 +457,39 @@ def check_SchreierSims_001(n): S.base(15) assert S.contains( - Perm([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + list(range(17, n))) + Perm( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + + list(range(17, n)) + ) ) assert not S.contains( - Perm([1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + list(range(17, n))) + Perm( + [1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + list(range(17, n)) + ) ) assert S.contains( - Perm([1, 0, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + list(range(17, n))) + Perm( + [1, 0, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + list(range(17, n)) + ) ) with pytest.raises(LibsemigroupsError): S.add_base_point(1) S.init() S.add_generator( - Perm([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + list(range(17, n))) + Perm( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0] + + list(range(17, n)) + ) ) assert S.size() == 17 S.add_generator( - Perm([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 14] + list(range(17, n))) + Perm( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 14] + + list(range(17, n)) + ) ) assert S.size() == 177843714048000 diff --git a/tests/test_sims.py b/tests/test_sims.py index e55e6a6df..59a2d24af 100644 --- a/tests/test_sims.py +++ b/tests/test_sims.py @@ -119,12 +119,16 @@ def test_sims1_000(): it = S.iterator(3) assert next(it) == WordGraph(3, [[0, 0]]) - S.number_of_threads(1).for_each(5, lambda wg: check_right_generating_pairs(S, wg)) + S.number_of_threads(1).for_each( + 5, lambda wg: check_right_generating_pairs(S, wg) + ) presentation.reverse(p) S = Sims1() assert S.presentation(p).number_of_congruences(5) == 9 for wg in S.iterator(5): - assert word_graph.follow_path(wg, 0, [1, 0, 1, 0]) == word_graph.follow_path(wg, 0, [0]) + assert word_graph.follow_path( + wg, 0, [1, 0, 1, 0] + ) == word_graph.follow_path(wg, 0, [0]) S.for_each(5, lambda wg: check_right_generating_pairs(S, wg)) mat = sims.poset(S, 5) assert mat == Matrix( @@ -332,7 +336,12 @@ def test_sims1_004(): assert T.number_of_congruences(16) == 13 orc = MinimalRepOrc() - d = orc.presentation(p).target_size(15).number_of_threads(os.cpu_count()).word_graph() + d = ( + orc.presentation(p) + .target_size(15) + .number_of_threads(os.cpu_count()) + .word_graph() + ) assert d.number_of_nodes() == 7 @@ -352,13 +361,21 @@ def test_sims_refiner_faithful_128(): S = Sims1() S.presentation(p) S.add_pruner(pruno) - assert S.number_of_threads(2).number_of_congruences(9) == 4 # Verified with GAP + assert ( + S.number_of_threads(2).number_of_congruences(9) == 4 + ) # Verified with GAP it = S.iterator(9) - assert next(it) == WordGraph(9, [[1, 2], [1, 3], [4, 5], [4, 4], [3, 1], [3, 0]]) - assert next(it) == WordGraph(9, [[1, 2], [3, 3], [4, 5], [1, 4], [4, 1], [3, 0]]) - assert next(it) == WordGraph(9, [[1, 2], [3, 4], [3, 5], [1, 1], [4, 3], [4, 0]]) + assert next(it) == WordGraph( + 9, [[1, 2], [1, 3], [4, 5], [4, 4], [3, 1], [3, 0]] + ) + assert next(it) == WordGraph( + 9, [[1, 2], [3, 3], [4, 5], [1, 4], [4, 1], [3, 0]] + ) + assert next(it) == WordGraph( + 9, [[1, 2], [3, 4], [3, 5], [1, 1], [4, 3], [4, 0]] + ) assert next(it) == WordGraph( 9, [ diff --git a/tests/test_stephen.py b/tests/test_stephen.py index 5fe6962e2..2e9f39e44 100644 --- a/tests/test_stephen.py +++ b/tests/test_stephen.py @@ -91,7 +91,12 @@ def verify_c4_normal_form(p, word, nf): S = Stephen(p) S.set_word(word).run() - assert sorted(list(stephen.words_accepted(S)), key=lexicographic_compare_key_func)[0] == nf + assert ( + sorted( + list(stephen.words_accepted(S)), key=lexicographic_compare_key_func + )[0] + == nf + ) assert all(stephen.accepts(S, w) for w in stephen.words_accepted(S)) assert stephen.number_of_words_accepted(S) == len(stephen.words_accepted(S)) @@ -186,18 +191,25 @@ def test_stephen_001(): [1, 1, 0, 0, 1], ] assert stephen.number_of_left_factors(s) == POSITIVE_INFINITY - assert all(stephen.is_left_factor(s, ww) for ww in islice(stephen.left_factors(s), 10)) + assert all( + stephen.is_left_factor(s, ww) + for ww in islice(stephen.left_factors(s), 10) + ) s.set_word([0, 0]).run() assert s.word_graph().number_of_nodes() == 5 - assert s.word_graph() == WordGraph(5, [[1, UNDEFINED], [2, 3], [1, 4], [4, 1], [3, 2]]) + assert s.word_graph() == WordGraph( + 5, [[1, UNDEFINED], [2, 3], [1, 4], [4, 1], [3, 2]] + ) p.rules = [] presentation.add_rule(p, [0, 0, 0], [0]) presentation.add_rule(p, [1, 1, 1], [1]) s.init(p).set_word([0, 0]).run() assert s.word() == [0, 0] - assert s.word_graph() == WordGraph(3, [[1, UNDEFINED], [2, UNDEFINED], [1, UNDEFINED]]) + assert s.word_graph() == WordGraph( + 3, [[1, UNDEFINED], [2, UNDEFINED], [1, UNDEFINED]] + ) @pytest.mark.quick @@ -546,9 +558,9 @@ def test_stephen_008(): to_word("dgabcdg"), ] - assert sorted(list(stephen.words_accepted(S)), key=lexicographic_compare_key_func)[ - 0 - ] == to_word("dfabcdf") + assert sorted( + list(stephen.words_accepted(S)), key=lexicographic_compare_key_func + )[0] == to_word("dfabcdf") assert all(stephen.accepts(S, w) for w in stephen.words_accepted(S)) assert stephen.number_of_words_accepted(S) == len(stephen.words_accepted(S)) @@ -556,7 +568,9 @@ def test_stephen_008(): S.set_word(to_word("abcdfceg")).run() assert stephen.number_of_words_accepted(S) == 16 - assert sorted(list(stephen.words_accepted(S)), key=lexicographic_compare_key_func) == [ + assert sorted( + list(stephen.words_accepted(S)), key=lexicographic_compare_key_func + ) == [ to_word("abcdfabcdf"), to_word("abcdfabcdg"), to_word("abcdfcef"), @@ -575,9 +589,9 @@ def test_stephen_008(): to_word("cegceg"), ] - assert sorted(list(stephen.words_accepted(S)), key=lexicographic_compare_key_func)[ - 0 - ] == to_word("abcdfabcdf") + assert sorted( + list(stephen.words_accepted(S)), key=lexicographic_compare_key_func + )[0] == to_word("abcdfabcdf") assert stephen.accepts(S, to_word("abcdfabcdf")) @@ -708,8 +722,12 @@ def test_stephen_014(): to_word = ToWord("abcd") p = Presentation(to_word("abcd")) presentation.add_rule(p, to_word("abbba"), to_word("cdc")) - verify_c4_normal_form(p, to_word("cdcdcabbbabbbabbcd"), to_word("abbbadcabbbabbbabbcd")) - verify_c4_equal_to(p, to_word("cdcdcabbbabbbabbcd"), to_word("abbbadcabbbabbbabbcd")) + verify_c4_normal_form( + p, to_word("cdcdcabbbabbbabbcd"), to_word("abbbadcabbbabbbabbcd") + ) + verify_c4_equal_to( + p, to_word("cdcdcabbbabbbabbcd"), to_word("abbbadcabbbabbbabbcd") + ) verify_c4_equal_to(p, to_word("abbbadcbbba"), to_word("cdabbbcdc")) verify_c4_equal_to(p, to_word("cdabbbcdc"), to_word("cdabbbcdc")) @@ -762,8 +780,12 @@ def test_stephen_017(): p = Presentation(to_word("abcd")) presentation.add_rule(p, to_word("abcd"), to_word("accca")) - verify_c4_normal_form(p, to_word("bbcabcdaccaccabcddd"), to_word("bbcabcdaccaccabcddd")) - verify_c4_equal_to(p, to_word("bbcabcdaccaccabcddd"), to_word("bbcabcdaccaccabcddd")) + verify_c4_normal_form( + p, to_word("bbcabcdaccaccabcddd"), to_word("bbcabcdaccaccabcddd") + ) + verify_c4_equal_to( + p, to_word("bbcabcdaccaccabcddd"), to_word("bbcabcdaccaccabcddd") + ) @pytest.mark.quick @@ -786,8 +808,12 @@ def test_stephen_019(): p = Presentation(to_word("abc")) presentation.add_rule(p, to_word("ccab"), to_word("cbac")) - verify_c4_normal_form(p, to_word("bacbaccabccabcbacbac"), to_word("bacbacbaccbaccbacbac")) - verify_c4_equal_to(p, to_word("bacbaccabccabcbacbac"), to_word("bacbacbaccbaccbacbac")) + verify_c4_normal_form( + p, to_word("bacbaccabccabcbacbac"), to_word("bacbacbaccbaccbacbac") + ) + verify_c4_equal_to( + p, to_word("bacbaccabccabcbacbac"), to_word("bacbacbaccbaccbacbac") + ) verify_c4_normal_form(p, to_word("ccabcbaccab"), to_word("cbaccbacbac")) verify_c4_equal_to(p, to_word("ccabcbaccab"), to_word("cbaccbacbac")) @@ -862,8 +888,12 @@ def test_stephen_025(): ReportGuard(False) to_word = ToWord("abcd") p = Presentation(to_word("abcd")) - presentation.add_rule(p, to_word("ababbabbbabbbb"), to_word("abbbbbabbbbbbabbbbbbbabbbbbbbb")) - presentation.add_rule(p, to_word("cdcddcdddcdddd"), to_word("cdddddcddddddcdddddddcdddddddd")) + presentation.add_rule( + p, to_word("ababbabbbabbbb"), to_word("abbbbbabbbbbbabbbbbbbabbbbbbbb") + ) + presentation.add_rule( + p, to_word("cdcddcdddcdddd"), to_word("cdddddcddddddcdddddddcdddddddd") + ) verify_c4_normal_form( p, @@ -930,7 +960,9 @@ def test_stephen_029(): p = Presentation(to_word("abcd")) presentation.add_rule(p, to_word("acba"), to_word("aabc")) presentation.add_rule(p, to_word("acba"), to_word("dbbd")) - verify_c4_normal_form(p, to_word("bbacbcaaabcbbd"), to_word("bbacbcaaabcbbd")) + verify_c4_normal_form( + p, to_word("bbacbcaaabcbbd"), to_word("bbacbcaaabcbbd") + ) verify_c4_normal_form(p, to_word("acbacba"), to_word("aabcabc")) verify_c4_normal_form(p, to_word("aabcabc"), to_word("aabcabc")) @@ -1510,17 +1542,25 @@ def test_stephen_049(): presentation.add_rule(p, [0, 0, 1], [1, 0]) S = Stephen(Presentation([])) - assert repr(S) == f"" + assert ( + repr(S) + == f"" + ) S.init(p) - assert repr(S) == f"" + assert ( + repr(S) + == f"" + ) S.set_word([0, 1, 1, 0]) assert ( - repr(S) == f"" ) S.run() assert ( - repr(S) == f"" ) S.set_word([0, 1, 1, 0, 0, 1, 1, 0, 0]) @@ -1538,17 +1578,22 @@ def test_stephen_049(): S.set_word([0, 1, 1, 0, 0, 1, 1, 0, 0, 1]) S.run() assert ( - repr(S) == f"" ) S.set_word([0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0]) S.run() assert ( - repr(S) == f"" ) S.init(p) - assert repr(S) == f"" + assert ( + repr(S) + == f"" + ) to_word = ToWord("abcABC") pi = InversePresentation(to_word("abcABC")) @@ -1558,9 +1603,15 @@ def test_stephen_049(): presentation.add_rule(pi, to_word("bc"), to_word("cb")) IS = Stephen(InversePresentation([])) - assert repr(IS) == f"" + assert ( + repr(IS) + == f"" + ) IS.init(pi) - assert repr(IS) == f"" + assert ( + repr(IS) + == f"" + ) IS.set_word(to_word("BaAbaBcAb")) assert ( repr(IS) diff --git a/tests/test_to.py b/tests/test_to.py index 46cdeb272..8d331ee12 100644 --- a/tests/test_to.py +++ b/tests/test_to.py @@ -69,7 +69,11 @@ def sample_to_str(i): def sample_to_int(x): - return "mnbvcxzlkjhgfdsapoiuytrewqMNBVCXZLKJHGFDSAPOIUYTREWQ5432167890".index(x) + return ( + "mnbvcxzlkjhgfdsapoiuytrewqMNBVCXZLKJHGFDSAPOIUYTREWQ5432167890".index( + x + ) + ) def sample_froidure_pin(): @@ -179,7 +183,9 @@ def check_froidure_pin_to_congruence(Word): def test_to_FroidurePin_000(): - fp = check_cong_to_froidure_pin(KnuthBendix, str, Rewriter="RewriteFromLeft") + fp = check_cong_to_froidure_pin( + KnuthBendix, str, Rewriter="RewriteFromLeft" + ) assert isinstance(to_cxx(fp), FroidurePinKBERewriteFromLeft) @@ -189,7 +195,9 @@ def test_to_FroidurePin_001(): def test_to_FroidurePin_002(): - fp = check_cong_to_froidure_pin(KnuthBendix, int, Rewriter="RewriteFromLeft") + fp = check_cong_to_froidure_pin( + KnuthBendix, int, Rewriter="RewriteFromLeft" + ) assert isinstance(to_cxx(fp), FroidurePinKBERewriteFromLeft) @@ -306,7 +314,9 @@ def test_to_FroidurePin_013(): def test_to_ToddCoxeter_014(): - tc = check_cong_to_todd_coxeter(KnuthBendix, str, Rewriter="RewriteFromLeft") + tc = check_cong_to_todd_coxeter( + KnuthBendix, str, Rewriter="RewriteFromLeft" + ) assert isinstance(tc, ToddCoxeter) assert tc.py_template_params == (str,) @@ -318,7 +328,9 @@ def test_to_ToddCoxeter_015(): def test_to_ToddCoxeter_016(): - tc = check_cong_to_todd_coxeter(KnuthBendix, int, Rewriter="RewriteFromLeft") + tc = check_cong_to_todd_coxeter( + KnuthBendix, int, Rewriter="RewriteFromLeft" + ) assert isinstance(tc, ToddCoxeter) assert tc.py_template_params == (list[int],) @@ -704,13 +716,15 @@ def test_to_InversePresentation_032(): assert iq.inverses() == [3, 4, 5, 0, 1, 2] assert iq.rules == q.rules - assert to(to(p, Return=(Presentation, list[int])), Return=(InversePresentation,)) == to( + assert to( + to(p, Return=(Presentation, list[int])), Return=(InversePresentation,) + ) == to( to(p, Return=(InversePresentation,)), Return=(Presentation, list[int]) ) - assert to(to(q, Return=(Presentation, str)), Return=(InversePresentation,)) == to( - to(q, Return=(InversePresentation,)), Return=(Presentation, str) - ) + assert to( + to(q, Return=(Presentation, str)), Return=(InversePresentation,) + ) == to(to(q, Return=(InversePresentation,)), Return=(Presentation, str)) ############################################################################### diff --git a/tests/test_ukkonen.py b/tests/test_ukkonen.py index 4ceac6e07..8e19294d9 100644 --- a/tests/test_ukkonen.py +++ b/tests/test_ukkonen.py @@ -233,7 +233,9 @@ def test_004(): assert ukkonen.number_of_pieces(t, [2]) == 1 assert ukkonen.pieces(t, [2]) == [[2]] - ukkonen.add_words(t, [[0, 1, 2, 8, 4, 5, 6, 7], [0, 1, 2], [8, 4, 5], [5, 6], [5, 6, 7]]) + ukkonen.add_words( + t, [[0, 1, 2, 8, 4, 5, 6, 7], [0, 1, 2], [8, 4, 5], [5, 6], [5, 6, 7]] + ) assert t.number_of_distinct_words() == 8 assert t.number_of_words() == 9 From e62becd160a8dc019295be6e9e35a7ec97bf8612 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 7 Jun 2025 14:37:45 +0100 Subject: [PATCH 10/11] Remove redundant explicit type for from_int --- src/action.cpp | 2 +- src/aho-corasick.cpp | 4 +--- src/cong-common.cpp | 4 +--- src/froidure-pin.cpp | 4 ++-- src/paths.cpp | 6 ++---- src/present.cpp | 3 +-- src/stephen.cpp | 6 ++---- src/todd-coxeter.cpp | 2 +- src/ukkonen.cpp | 8 +++----- src/word-graph.cpp | 4 ++-- 10 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/action.cpp b/src/action.cpp index 058087e2b..6f93f9bab 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -232,7 +232,7 @@ Returns an iterator yielding the generators. thing.def( "position", [](Action_& self, const_reference_point_type pt) { - return from_int(self.position(pt)); + return from_int(self.position(pt)); }, py::arg("pt"), R"pbdoc( diff --git a/src/aho-corasick.cpp b/src/aho-corasick.cpp index 387b548df..981f72017 100644 --- a/src/aho-corasick.cpp +++ b/src/aho-corasick.cpp @@ -90,9 +90,7 @@ Copy a :any:`AhoCorasick` object. "child", [](AhoCorasick const& self, AhoCorasick::index_type parent, - letter_type letter) { - return from_int(self.child(parent, letter)); - }, + letter_type letter) { return from_int(self.child(parent, letter)); }, py::arg("parent"), py::arg("letter"), R"pbdoc( diff --git a/src/cong-common.cpp b/src/cong-common.cpp index d2aba5512..1908a1824 100644 --- a/src/cong-common.cpp +++ b/src/cong-common.cpp @@ -409,9 +409,7 @@ Copy a :any:`{name}` object. doc extra_doc) { thing.def( "number_of_classes", - [](Thing& self) { - return from_int(self.number_of_classes()); - }, + [](Thing& self) { return from_int(self.number_of_classes()); }, make_doc(R"pbdoc( :sig=(self: {name}) -> int | PositiveInfinity: {only_document_once} diff --git a/src/froidure-pin.cpp b/src/froidure-pin.cpp index 05bb49df3..a822c3a60 100644 --- a/src/froidure-pin.cpp +++ b/src/froidure-pin.cpp @@ -206,7 +206,7 @@ See :any:`add_generator` for a detailed description. thing.def( "current_position", [](FroidurePin_ const& self, Element const& x) { - return from_int(self.current_position(x)); + return from_int(self.current_position(x)); }, py::arg("x").noconvert(), R"pbdoc( @@ -713,7 +713,7 @@ elements are sorted, or :any:`UNDEFINED` if *i* is greater than m.def( "froidure_pin_current_position", [](FroidurePinBase const& fpb, word_type const& w) { - return from_int(froidure_pin::current_position(fpb, w)); + return from_int(froidure_pin::current_position(fpb, w)); }, py::arg("fpb"), py::arg("w"), diff --git a/src/paths.cpp b/src/paths.cpp index 9042c3648..69e2ba244 100644 --- a/src/paths.cpp +++ b/src/paths.cpp @@ -150,7 +150,7 @@ are no more paths in the range, and ``False`` otherwise. "count", [](Paths_& p) { p.throw_if_source_undefined(); - return from_int(p.count()); + return from_int(p.count()); }, R"pbdoc( :sig=(self: Paths) -> int | PositiveInfinity: @@ -200,9 +200,7 @@ Get the current path in the range. )pbdoc"); thing1.def( "max", - [](Paths_ const& self) { - return from_int(self.max()); - }, + [](Paths_ const& self) { return from_int(self.max()); }, R"pbdoc( :sig=(self: Paths) -> int | PositiveInfinity: :only-document-once: diff --git a/src/present.cpp b/src/present.cpp index 1e58b18cf..772567c9b 100644 --- a/src/present.cpp +++ b/src/present.cpp @@ -823,8 +823,7 @@ If no such word can be found, then a word of length :math:`0` is returned. m.def( "make_semigroup", [](Presentation_& p) { - using letter_type = Presentation_::letter_type; - return from_int(presentation::make_semigroup(p)); + return from_int(presentation::make_semigroup(p)); }, py::arg("p"), R"pbdoc( diff --git a/src/stephen.cpp b/src/stephen.cpp index def5e7b32..a26e72f75 100644 --- a/src/stephen.cpp +++ b/src/stephen.cpp @@ -397,8 +397,7 @@ This function triggers the algorithm implemented in this class (if it hasn't bee m.def( "stephen_number_of_left_factors", [](Stephen_& s, size_t min, size_t max) { - return from_int( - stephen::number_of_left_factors(s, min, max)); + return from_int(stephen::number_of_left_factors(s, min, max)); }, py::arg("s"), py::arg("min") = 0, @@ -440,8 +439,7 @@ in the range *min* to *max*. m.def( "stephen_number_of_words_accepted", [](Stephen_& s, size_t min, size_t max) { - return from_int( - stephen::number_of_words_accepted(s, min, max)); + return from_int(stephen::number_of_words_accepted(s, min, max)); }, py::arg("s"), py::arg("min") = 0, diff --git a/src/todd-coxeter.cpp b/src/todd-coxeter.cpp index b5d630db5..2f8ba1dbb 100644 --- a/src/todd-coxeter.cpp +++ b/src/todd-coxeter.cpp @@ -242,7 +242,7 @@ semigroup. thing.def( "current_index_of", [](ToddCoxeter_ const& self, Word const& w) { - return from_int(todd_coxeter::current_index_of(self, w)); + return from_int(todd_coxeter::current_index_of(self, w)); }, py::arg("w"), R"pbdoc( diff --git a/src/ukkonen.cpp b/src/ukkonen.cpp index 52bafaba0..9d102f064 100644 --- a/src/ukkonen.cpp +++ b/src/ukkonen.cpp @@ -41,7 +41,7 @@ namespace libsemigroups { uk.def( "index", [](Ukkonen const& self, Word const& w) { - return from_int(self.index(w.begin(), w.end())); + return from_int(self.index(w.begin(), w.end())); }, py::arg("w"), R"pbdoc( @@ -378,7 +378,7 @@ contained in *u*. If no such suffix exists, then an empty word is returned. m.def( "number_of_pieces", [](Ukkonen const& u, Word const& w) { - return from_int(ukkonen::number_of_pieces(u, w)); + return from_int(ukkonen::number_of_pieces(u, w)); }, py::arg("u"), py::arg("w"), @@ -621,9 +621,7 @@ The index of the first letter in the edge leading to the node. // TODO should the others here also be def_property_readonly? node.def_property_readonly( "parent", - [](Ukkonen::Node const& node) { - return from_int(node.parent); - }, + [](Ukkonen::Node const& node) { return from_int(node.parent); }, R"pbdoc( The index of the parent node. )pbdoc"); diff --git a/src/word-graph.cpp b/src/word-graph.cpp index 2b844d575..2938fec72 100644 --- a/src/word-graph.cpp +++ b/src/word-graph.cpp @@ -200,7 +200,7 @@ the word graph. [](WordGraph_ const& self, node_type source) { auto result = (self.targets(source) | rx::transform([](node_type target) { - return from_int(target); + return from_int(target); })); return py::make_iterator(rx::begin(result), rx::end(result)); }, @@ -552,7 +552,7 @@ out_degree())`` , then this function adds an edge from *a* to *b* labelled *a*. thing.def( "target", [](WordGraph_ const& self, node_type source, label_type a) { - return from_int(self.target(source, a)); + return from_int(self.target(source, a)); }, py::arg("source"), py::arg("a"), From 5527d4fc9806c14d8b1311e4e7221843e1d1c04a Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Sat, 7 Jun 2025 15:00:05 +0100 Subject: [PATCH 11/11] Add missing from_ints overload --- src/main.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.hpp b/src/main.hpp index c05ed05f3..8f700ee00 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -181,6 +181,12 @@ namespace libsemigroups { return {from_int(pair.first), from_int(pair.second)}; } + template + std::tuple, int_or_constant> + from_ints(std::tuple const& tup) { + return {from_int(std::get<0>(tup)), from_int(std::get<1>(tup))}; + } + } // namespace libsemigroups #endif // SRC_MAIN_HPP_