From 6c1015cc821b7562d0f382b13130720dd9b4ba43 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Mon, 9 Jun 2025 09:45:34 +0100 Subject: [PATCH 1/3] Fix coverage make target --- Makefile | 3 +-- tests/__init__.py | 0 tests/runner.py | 4 ++-- tests/test_froidure_pin.py | 2 +- tests/test_knuth_bendix.py | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 tests/__init__.py diff --git a/Makefile b/Makefile index b5110522f..5b21c575e 100644 --- a/Makefile +++ b/Makefile @@ -28,11 +28,10 @@ lint: etc/make-lint.sh coverage: - @coverage run --source . --omit="tests/*" -m pytest tests/test_*.py + @coverage run --source libsemigroups_pybind11 --omit="tests/*" -m pytest tests/test_*.py @coverage html @echo "See: htmlcov/index.html" - clean-doc: rm -rf docs/_build diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/runner.py b/tests/runner.py index 29c1c1685..aab27d975 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -9,8 +9,8 @@ # The full license is in the file LICENSE, distributed with this software. """ -This module contains some functions used in tests for FpSemigroupInterface -derived classes, i.e. KnuthBendix, FpSemigroup, etc. +This module contains some functions used in tests for derived classes, i.e. +KnuthBendix, etc. """ from datetime import timedelta diff --git a/tests/test_froidure_pin.py b/tests/test_froidure_pin.py index e807c70aa..df33a74cd 100644 --- a/tests/test_froidure_pin.py +++ b/tests/test_froidure_pin.py @@ -14,7 +14,7 @@ from datetime import timedelta import pytest -from runner import check_runner +from .runner import check_runner from libsemigroups_pybind11 import ( FroidurePin, diff --git a/tests/test_knuth_bendix.py b/tests/test_knuth_bendix.py index 781c56f1e..861ee5d0b 100644 --- a/tests/test_knuth_bendix.py +++ b/tests/test_knuth_bendix.py @@ -14,7 +14,7 @@ from datetime import timedelta import pytest -from runner import check_runner +from .runner import check_runner from libsemigroups_pybind11 import ( KnuthBendix, congruence_kind, From d7e05a0665d5484c2e96c296caeb1621862f84d9 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Tue, 10 Jun 2025 11:14:21 +0100 Subject: [PATCH 2/3] presentation-examples: reword notes --- src/presentation-examples.cpp | 162 +++++++++++++++++----------------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/src/presentation-examples.cpp b/src/presentation-examples.cpp index 86615e95e..213adfa1c 100644 --- a/src/presentation-examples.cpp +++ b/src/presentation-examples.cpp @@ -949,10 +949,10 @@ This function returns a monoid presentation defining the stellar monoid with .. note:: - This function performs exactly the same as :any:`stellar_monoid_GH19`, + This function returns exactly the same presentation as :any:`stellar_monoid_GH19`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_dual_symmetric_inverse_monoid", &examples::dual_symmetric_inverse_monoid, @@ -974,10 +974,10 @@ monoid of degree *n*, as in Section 3 of :cite:`Easdown2007aa`. .. note:: - This function performs exactly the same as :any:`dual_symmetric_inverse_monoid`, + This function returns exactly the same presentation as :any:`dual_symmetric_inverse_monoid`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_uniform_block_bijection_monoid", &examples::uniform_block_bijection_monoid, @@ -999,10 +999,10 @@ monoid of degree *n*, as in :cite:`FitzGerald2003aa`. .. note:: - This function performs exactly the same as :any:`uniform_block_bijection_monoid_Fit03`, + This function returns exactly the same presentation as :any:`uniform_block_bijection_monoid_Fit03`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_partition_monoid", &examples::partition_monoid, @@ -1050,10 +1050,10 @@ monoid of degree *n*, as in Theorem 5 of :cite:`Maltcev2007aa`. .. note:: - This function performs exactly the same as :any:`singular_brauer_monoid_MM07`, + This function returns exactly the same presentation as :any:`singular_brauer_monoid_MM07`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_orientation_preserving_monoid", &examples::orientation_preserving_monoid, @@ -1076,10 +1076,10 @@ preserving mappings on a finite chain of order *n*, as described in .. note:: - This function performs exactly the same as :any:`orientation_preserving_monoid_AR00`, + This function returns exactly the same presentation as :any:`orientation_preserving_monoid_AR00`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_orientation_preserving_reversing_monoid", &examples::orientation_preserving_reversing_monoid, @@ -1102,10 +1102,10 @@ preserving or reversing mappings on a finite chain of order *n*, as described in .. note:: - This function performs exactly the same as :any:`orientation_preserving_reversing_monoid_AR00`, + This function returns exactly the same presentation as :any:`orientation_preserving_reversing_monoid_AR00`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_temperley_lieb_monoid", &examples::temperley_lieb_monoid, @@ -1127,10 +1127,10 @@ with *n* generators, as described in Theorem 2.2 of :cite:`East2022aa`. .. note:: - This function performs exactly the same as :any:`temperley_lieb_monoid_Eas21`, + This function returns exactly the same presentation as :any:`temperley_lieb_monoid_Eas21`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_brauer_monoid", &examples::brauer_monoid, @@ -1152,10 +1152,10 @@ degree *n*, as described in Theorem 3.1 of :cite:`Kudryavtseva2006aa`. .. note:: - This function performs exactly the same as :any:`brauer_monoid_KM07`, + This function returns exactly the same presentation as :any:`brauer_monoid_KM07`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_partial_brauer_monoid", &examples::partial_brauer_monoid, @@ -1177,10 +1177,10 @@ of degree *n*, as described in Theorem 5.1 of :cite:`Kudryavtseva2006aa`. .. note:: - This function performs exactly the same as :any:`partial_brauer_monoid_KM07`, + This function returns exactly the same presentation as :any:`partial_brauer_monoid_KM07`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_motzkin_monoid", &examples::motzkin_monoid, @@ -1204,10 +1204,10 @@ Lemma 4.10 which rendered the presentation as stated in the paper incorrect. .. note:: - This function performs exactly the same as :any:`motzkin_monoid_PHL13`, + This function returns exactly the same presentation as :any:`motzkin_monoid_PHL13`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_fibonacci_semigroup", &examples::fibonacci_semigroup, @@ -1233,10 +1233,10 @@ This function returns a semigroup presentation defining the Fibonacci semigroup .. note:: - This function performs exactly the same as :any:`fibonacci_semigroup_CRRT94`, + This function returns exactly the same presentation as :any:`fibonacci_semigroup_CRRT94`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. :returns: The specified presentation. @@ -1263,10 +1263,10 @@ This function returns a monoid presentation defining the plactic monoid with .. note:: - This function performs exactly the same as :any:`plactic_monoid_Knu70`, + This function returns exactly the same presentation as :any:`plactic_monoid_Knu70`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_stylic_monoid", &examples::stylic_monoid, @@ -1288,10 +1288,10 @@ This function returns a monoid presentation defining the stylic monoid with .. note:: - This function performs exactly the same as :any:`stylic_monoid_AR22`, + This function returns exactly the same presentation as :any:`stylic_monoid_AR22`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_symmetric_group", &examples::symmetric_group, @@ -1342,10 +1342,10 @@ degree *n*, as in Theorem B of :cite:`Moore1897aa`. .. note:: - This function performs exactly the same as :any:`alternating_group_Moo97`, + This function returns exactly the same presentation as :any:`alternating_group_Moo97`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_rectangular_band", &examples::rectangular_band, @@ -1372,10 +1372,10 @@ rectangular band, as given in Proposition 4.2 of :cite:`Ayik2000aa`. .. note:: - This function performs exactly the same as :any:`rectangular_band_ACOR00`, + This function returns exactly the same presentation as :any:`rectangular_band_ACOR00`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_full_transformation_monoid", &examples::full_transformation_monoid, @@ -1480,10 +1480,10 @@ generators, as in :cite:`Cassaigne2001aa`. .. note:: - This function performs exactly the same as :any:`chinese_monoid_CEKNH01`, + This function returns exactly the same presentation as :any:`chinese_monoid_CEKNH01`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_monogenic_semigroup", &examples::monogenic_semigroup, @@ -1532,10 +1532,10 @@ order preserving transformations of degree *n*, as described in Section 2 of .. note:: - This function performs exactly the same as :any:`order_preserving_monoid_AR00`, + This function returns exactly the same presentation as :any:`order_preserving_monoid_AR00`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_cyclic_inverse_monoid", &examples::cyclic_inverse_monoid, @@ -1587,10 +1587,10 @@ of the cyclic inverse monoid of degree *n*, as in Theorem 2.17 of .. note:: - This function performs exactly the same as :any:`order_preserving_cyclic_inverse_monoid_Fer22`, + This function returns exactly the same presentation as :any:`order_preserving_cyclic_inverse_monoid_Fer22`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_partial_isometries_cycle_graph_monoid", &examples::partial_isometries_cycle_graph_monoid, @@ -1613,10 +1613,10 @@ isometries of an :math:`n`-cycle graph, as in Theorem 2.8 of .. note:: - This function performs exactly the same as :any:`partial_isometries_cycle_graph_monoid_FP22`, + This function returns exactly the same presentation as :any:`partial_isometries_cycle_graph_monoid_FP22`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_not_symmetric_group", &examples::not_symmetric_group, @@ -1639,10 +1639,10 @@ symmetric group of degree *n*, but does not, as in Section 2.2 of .. note:: - This function performs exactly the same as :any:`not_symmetric_group_GKKL08`, + This function returns exactly the same presentation as :any:`not_symmetric_group_GKKL08`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_special_linear_group_2", &examples::special_linear_group_2, @@ -1667,10 +1667,10 @@ where *q* is an odd prime, as in Theorem 4 of :cite:`Campbell1980aa`. .. note:: - This function performs exactly the same as :any:`special_linear_group_2_CR80`, + This function returns exactly the same presentation as :any:`special_linear_group_2_CR80`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_hypo_plactic_monoid", &examples::hypo_plactic_monoid, @@ -1695,10 +1695,10 @@ includes the rules from :any:`plactic_monoid_Knu70`. .. note:: - This function performs exactly the same as :any:`hypo_plactic_monoid_Nov00`, + This function returns exactly the same presentation as :any:`hypo_plactic_monoid_Nov00`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_sigma_plactic_monoid", &examples::sigma_plactic_monoid, @@ -1745,10 +1745,10 @@ This function returns a presentation for the :math:`0`-rook monoid of degree .. note:: - This function performs exactly the same as :any:`zero_rook_monoid_Gay18`, + This function returns exactly the same presentation as :any:`zero_rook_monoid_Gay18`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_renner_type_B_monoid", &examples::renner_type_B_monoid, @@ -1779,10 +1779,10 @@ When ``q == 1``, this corresponds to Theorem 8.4.19 of :cite:`Gay2018aa`. .. note:: - This function performs exactly the same as :any:`renner_type_B_monoid_Gay18`, + This function returns exactly the same presentation as :any:`renner_type_B_monoid_Gay18`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_not_renner_type_B_monoid", &examples::not_renner_type_B_monoid, @@ -1812,10 +1812,10 @@ When ``q == 1``, this corresponds to Section 3.2 of :cite:`Godelle2009aa`. .. note:: - This function performs exactly the same as :any:`not_renner_type_B_monoid_Gay18`, + This function returns exactly the same presentation as :any:`not_renner_type_B_monoid_Gay18`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_renner_type_D_monoid", &examples::renner_type_D_monoid, @@ -1845,10 +1845,10 @@ When ``q == 1``, this corresponds to Theorem 8.4.43 of :cite:`Gay2018aa`. .. note:: - This function performs exactly the same as :any:`renner_type_D_monoid_Gay18`, + This function returns exactly the same presentation as :any:`renner_type_D_monoid_Gay18`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); m.def("presentation_examples_not_renner_type_D_monoid", &examples::not_renner_type_D_monoid, @@ -1876,10 +1876,10 @@ When ``q == 1``, this corresponds to Section 3.3 of :cite:`Godelle2009aa`. .. note:: - This function performs exactly the same as :any:`not_renner_type_D_monoid_God09`, + This function returns exactly the same presentation as :any:`not_renner_type_D_monoid_God09`, and exists as a convenience function for when a presentation for the - alternating group is required, but the relations of the presentation - are not important. + alternating group is required, but the specific presentation + is not important. )pbdoc"); } // init_presentation_examples From 62e7f636849dddb59bd8e9d767a7f17b38a4bf03 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Mon, 9 Jun 2025 10:27:03 +0100 Subject: [PATCH 3/3] Check/fix return_value_policy for libsemigroups objects --- .../visualisation/dot.edge.rst | 14 ++ .../visualisation/dot.kind.rst | 14 ++ .../visualisation/dot.node.rst | 13 ++ .../data-structures/visualisation/dot.rst | 6 +- .../data-structures/visualisation/index.rst | 3 + src/aho-corasick.cpp | 96 ++++++------ src/bipart.cpp | 33 +++- src/bmat8.cpp | 148 ++++++++++-------- src/dot.cpp | 100 +++++++++--- src/forest.cpp | 16 +- src/froidure-pin-base.cpp | 14 +- src/froidure-pin.cpp | 51 ++++-- src/gabow.cpp | 23 ++- src/kambites.cpp | 4 +- src/knuth-bendix-impl.cpp | 31 ++-- src/knuth-bendix.cpp | 25 ++- src/konieczny.cpp | 30 ++-- src/libsemigroups_pybind11/adapters.py | 4 +- src/libsemigroups_pybind11/aho_corasick.py | 4 +- src/libsemigroups_pybind11/sims.py | 2 +- src/matrix.cpp | 20 ++- src/paths.cpp | 9 +- src/present.cpp | 44 +++--- src/runner.cpp | 2 +- src/schreier-sims.cpp | 7 +- src/sims.cpp | 12 +- src/stephen.cpp | 2 + src/todd-coxeter-impl.cpp | 13 +- src/todd-coxeter.cpp | 10 +- src/transf.cpp | 14 +- src/ukkonen.cpp | 125 ++++++++------- src/word-graph.cpp | 28 ++-- src/word-range.cpp | 104 ++---------- tests/cong_common.py | 37 +++++ tests/test_action.py | 11 ++ tests/test_aho_corasick.py | 5 + tests/test_bipart.py | 8 + tests/test_cong.py | 8 + tests/test_dot.py | 24 +++ tests/test_forest.py | 33 ++++ tests/test_froidure_pin.py | 37 ++++- tests/test_gabow.py | 28 +++- tests/test_kambites.py | 19 ++- tests/test_knuth_bendix.py | 36 ++++- tests/test_konieczny.py | 24 ++- tests/test_matrix.py | 22 +++ tests/test_paths.py | 15 ++ tests/test_pbr.py | 6 + tests/test_present.py | 26 +++ tests/test_runner.py | 3 +- tests/test_schreier_sims.py | 27 +++- tests/test_sims.py | 79 +++++++++- tests/test_stephen.py | 16 ++ tests/test_todd_coxeter.py | 45 ++++++ tests/test_transf.py | 8 + tests/test_ukkonen.py | 32 +++- tests/test_word_graph.py | 28 ++++ tests/{test_words.py => test_word_range.py} | 35 ++++- 58 files changed, 1165 insertions(+), 468 deletions(-) create mode 100644 docs/source/data-structures/visualisation/dot.edge.rst create mode 100644 docs/source/data-structures/visualisation/dot.kind.rst create mode 100644 docs/source/data-structures/visualisation/dot.node.rst create mode 100644 tests/cong_common.py create mode 100644 tests/test_forest.py rename tests/{test_words.py => test_word_range.py} (91%) diff --git a/docs/source/data-structures/visualisation/dot.edge.rst b/docs/source/data-structures/visualisation/dot.edge.rst new file mode 100644 index 000000000..0e508e5d0 --- /dev/null +++ b/docs/source/data-structures/visualisation/dot.edge.rst @@ -0,0 +1,14 @@ +.. + Copyright (c) 2024 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +The Dot.Edge class +================== + +.. autoclass:: libsemigroups_pybind11.Dot.Edge + :members: + :class-doc-from: class + :exclude-members: name diff --git a/docs/source/data-structures/visualisation/dot.kind.rst b/docs/source/data-structures/visualisation/dot.kind.rst new file mode 100644 index 000000000..94bb27f1f --- /dev/null +++ b/docs/source/data-structures/visualisation/dot.kind.rst @@ -0,0 +1,14 @@ +.. + Copyright (c) 2024 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +The Dot.Kind class +================== + +.. autoclass:: libsemigroups_pybind11.Dot.Kind + :members: + :class-doc-from: class + :exclude-members: name diff --git a/docs/source/data-structures/visualisation/dot.node.rst b/docs/source/data-structures/visualisation/dot.node.rst new file mode 100644 index 000000000..4bf67fcea --- /dev/null +++ b/docs/source/data-structures/visualisation/dot.node.rst @@ -0,0 +1,13 @@ +.. + Copyright (c) 2024 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +The Dot.Node class +================== + +.. autoclass:: libsemigroups_pybind11.Dot.Node + :members: + :class-doc-from: class diff --git a/docs/source/data-structures/visualisation/dot.rst b/docs/source/data-structures/visualisation/dot.rst index a4eb27e1d..55952606b 100644 --- a/docs/source/data-structures/visualisation/dot.rst +++ b/docs/source/data-structures/visualisation/dot.rst @@ -23,18 +23,19 @@ Contents ~Dot Dot.Edge Dot.Node - Dot.Kind Dot.add_attr Dot.add_edge Dot.add_node Dot.add_subgraph Dot.attrs Dot.colors + Dot.edge Dot.edges - Dot.nodes Dot.is_node Dot.kind Dot.name + Dot.node + Dot.nodes Dot.nodes Dot.subgraphs Dot.to_string @@ -45,3 +46,4 @@ Full API .. autoclass:: Dot :members: :class-doc-from: init + :exclude-members: Kind, Node, Edge diff --git a/docs/source/data-structures/visualisation/index.rst b/docs/source/data-structures/visualisation/index.rst index 11747eab5..f2e89be4a 100644 --- a/docs/source/data-structures/visualisation/index.rst +++ b/docs/source/data-structures/visualisation/index.rst @@ -20,6 +20,9 @@ language of `Graphviz `_ graph drawing software. :maxdepth: 1 dot + dot.edge + dot.kind + dot.node Functions for creating Dot objects ---------------------------------- diff --git a/src/aho-corasick.cpp b/src/aho-corasick.cpp index 981f72017..45e0dc465 100644 --- a/src/aho-corasick.cpp +++ b/src/aho-corasick.cpp @@ -132,10 +132,11 @@ Calculate the height of a node. :raises LibsemigroupsError: if ``throw_if_node_index_not_active(i)`` throws. -:complexity: Linear in the return value which is, at worst, the maximum length of a word in the trie +:complexity: + Linear in the return value which is, at worst, the maximum length + of a word in the trie .. seealso:: :any:`throw_if_node_index_not_active`. - )pbdoc"); thing.def("init", @@ -159,7 +160,6 @@ Returns the number of nodes in the trie. This function Returns the number of nodes in the trie. - :complexity: Constant :returns: The number of nodes> @@ -174,17 +174,16 @@ This function Returns the number of nodes in the trie. Find the signature of a node Return the the signature of the node with index *i*. Recall that the -*signature* of a node :math:`n` is the word consisting of the edge labels -of the unique path from the root to -:math:`n`. +*signature* of a node :math:`n` is the word consisting of the edge labels of +the unique path from the root to :math:`n`. -:param i: the index of the node whose signature is sought +:param i: the index of the node whose signature is sought. :type i: int -:returns: The signature +:returns: The signature. :rtype: list[int] -:complexity: Linear in the height of the node +:complexity: Linear in the height of the node. )pbdoc"); thing.def("suffix_link", @@ -304,14 +303,16 @@ This function checks if the node with index *i* is terminal or not. :complexity: Constant )pbdoc"); + //////////////////////////////////////////////////////////////////////// // Helpers + //////////////////////////////////////////////////////////////////////// + m.def("aho_corasick_add_word", &aho_corasick::add_word, py::arg("ac"), py::arg("w"), R"pbdoc( :sig=(ac: AhoCorasick, w: list[int] | str) -> int: -:only-document-once: Add a word to the trie of *ac* @@ -336,19 +337,20 @@ this function does nothing. .. seealso:: :any:`AhoCorasick.signature` )pbdoc"); + + // Documented above m.def("aho_corasick_add_word", &aho_corasick::add_word, py::arg("ac"), - py::arg("w"), - R"pbdoc( -:sig=(ac: AhoCorasick, w: list[int] | str) -> int: -TODO -)pbdoc"); - m.def("rm_word", + py::arg("w")); + + m.def("aho_corasick_rm_word", &aho_corasick::rm_word, py::arg("ac"), py::arg("w"), R"pbdoc( +:sig=(ac: AhoCorasick, w: list[int] | str) -> int: + Remove a word from the trie of *ac*. From the trie of *ac*, remove each node of the given word *w* that is not part of @@ -365,11 +367,11 @@ function makes :math`n` not terminal. If *w* does not correspond to a terminal node, then calling this function does nothing. -:param ac: object whose trie is to be removed from +:param ac: the trie. :type ac: AhoCorasick -:param w: the word to remove -:type w: list[int] +:param w: the word to remove. +:type w: list[int] | str :returns: The index corresponding to the node with signature equal to *w*. :rtype: int @@ -377,21 +379,16 @@ nothing. :complexity: Linear in the length of *w*. .. seealso:: :any:`AhoCorasick.signature` - )pbdoc"); - m.def("rm_word", + + // Documented above. + m.def("aho_corasick_rm_word", &aho_corasick::rm_word, py::arg("ac"), - py::arg("w"), - R"pbdoc( -Remove a word from the trie of *ac*. + py::arg("w")); -This function performs the same as ``rm_word(ac, w)``, -but *w* is a :any:`string` rather than list[:any:`int`]. - -)pbdoc"); m.def( - "traverse_word", + "aho_corasick_traverse_word", [](AhoCorasick const& ac, index_type start, word_type const& w) { return aho_corasick::traverse_word(ac, start, w); }, @@ -399,62 +396,59 @@ but *w* is a :any:`string` rather than list[:any:`int`]. py::arg("start"), py::arg("w"), R"pbdoc( +:sig=(ac: AhoCorasick, start: int, w: list[int] | str) -> int: + Traverse the trie of *ac* using suffix links where necessary. This function traverses the trie of *ac*, starting at the node with index *start*, and traversing using the letters in the word *w*. -:param ac: object to traverse. +:param ac: the trie to traverse. :type ac: AhoCorasick -:param start: the index of the node to first traverse from. +:param start: the index of the node to start traversing from. :type start: int -:param w: Word to traverse by -:type w: list[int] +:param w: the word to traverse. +:type w: list[int] | str -:returns: The result of the traversal +:returns: The index of the node reached by traversing. :rtype: int - )pbdoc"); + + // Documented above m.def( - "traverse_word", + "aho_corasick_traverse_word", [](AhoCorasick const& ac, index_type start, std::string const& w) { return aho_corasick::traverse_word(ac, start, w); }, py::arg("ac"), py::arg("start"), - py::arg("w"), - R"pbdoc( -Traverse the trie of *ac* using suffix links where necessary. + py::arg("w")); -This function performs the same as ``traverse_word(ac, w)``, -but *w* is a :any:`string` rather than list[:any:`int`]. -)pbdoc"); m.def( - "traverse_word", + "aho_corasick_traverse_word", [](AhoCorasick const& ac, word_type const& w) { return aho_corasick::traverse_word(ac, w); }, py::arg("ac"), py::arg("w"), R"pbdoc( +:sig=(ac: AhoCorasick, w: list[int] | str) -> int: + Traverse the trie of *ac* from the root using suffix links where necessary. -This function performs the same as ``traverse_word(ac, AhoCorasick.root, w)``. +This function is the same as ``traverse_word(ac, AhoCorasick.root, w)``. )pbdoc"); + + // Documented above m.def( - "traverse_word", + "aho_corasick_traverse_word", [](AhoCorasick const& ac, std::string const& w) { return aho_corasick::traverse_word(ac, w); }, py::arg("ac"), - py::arg("w"), - R"pbdoc( -Traverse the trie of *ac* from the root using suffix links where necessary. - -This function performs the same as ``traverse_word(ac, AhoCorasick.root, w)`` -)pbdoc"); + py::arg("w")); m.def( "aho_corasick_dot", diff --git a/src/bipart.cpp b/src/bipart.cpp index 9e7725210..d85b2cb00 100644 --- a/src/bipart.cpp +++ b/src/bipart.cpp @@ -51,6 +51,8 @@ bipartitions. This is the purpose of this class. thing.def(py::self == py::self); thing.def("__copy__", [](Blocks const& self) { return Blocks(self); }); + thing.def("__hash__", &Blocks::hash_value, py::is_operator()); + thing.def( "copy", [](Blocks const& self) { return Blocks(self); }, @@ -93,6 +95,7 @@ transverse and those consisting of positive values are not. :complexity: linear in the sum of the sizes of the vectors in blocks. )pbdoc"); + thing.def( "iterator", [](Blocks const& self) { @@ -109,6 +112,7 @@ Return a const iterator yielding the indices of the blocks. :rtype: Iterator[int] )pbdoc"); + thing.def("degree", &Blocks::degree, R"pbdoc( @@ -121,7 +125,6 @@ the ``blocks`` used to construct ``self``. :rtype: int )pbdoc"); - thing.def("__hash__", &Blocks::hash_value, py::is_operator()); thing.def( "is_transverse_block", @@ -140,7 +143,7 @@ unsigned). :type index: int :raises LibsemigroupsError: - if ``i`` is not in the range :math:`[0, n)` where + if *index* is not in the range :math:`[0, n)` where :math:`n` is the return value of :any:`number_of_blocks`. :complexity: Constant. @@ -148,12 +151,15 @@ unsigned). :returns: Whether or not the given block is transverse. :rtype: bool )pbdoc"); + thing.def("lookup", &Blocks::lookup, R"pbdoc( -Return the transverse blocks lookup. The value in -position ``i`` of the returned list is ``True`` if the block with -index ``i`` is transverse and ``False`` if it is not. +Returns the transverse blocks lookup. + +This function returns the transverse blocks lookup. The value in position ``i`` +of the returned list is ``True`` if the block with index ``i`` is transverse +and ``False`` if it is not. :complexity: Constant. @@ -163,6 +169,7 @@ index ``i`` is transverse and ``False`` if it is not. :rtype: list[bool] )pbdoc"); + thing.def("number_of_blocks", &Blocks::number_of_blocks, R"pbdoc( @@ -177,6 +184,7 @@ number of parts in the partition that instances of this class represent. :rtype: int )pbdoc"); + thing.def("rank", &Blocks::rank, R"pbdoc( @@ -191,6 +199,7 @@ of ``True`` values in :any:`lookup()`. :rtype: int )pbdoc"); + thing.def( "__getitem__", [](Blocks const& a, size_t b) { return a.at(b); }, @@ -284,6 +293,7 @@ The items in *blocks* should be: :raises LibsemigroupsError: if any of the conditions above is not met. )pbdoc"); + thing.def(py::init([](std::vector const& lookup) { return make(lookup); }), @@ -309,6 +319,7 @@ they are not. :raises LibsemigroupsError: if any of the conditions above is not met. )pbdoc"); + thing.def( "iterator", [](Bipartition const& self) { @@ -325,6 +336,7 @@ Return an iterator yielding the indices of the blocks. :rtype: Iterator[int] )pbdoc"); + thing.def("degree", &Bipartition::degree, R"pbdoc( @@ -338,6 +350,7 @@ A bipartition is of degree :math:`n` if it is a partition of :rtype: int )pbdoc"); + thing.def("is_transverse_block", &Bipartition::is_transverse_block, py::arg("index"), @@ -351,7 +364,7 @@ greater than :math:`n`, which is the degree of the bipartition. :type index: int :raises LibsemigroupsError: - if ``index`` is not in the range from ``0`` to + if *index* is not in the range from ``0`` to :any:`number_of_left_blocks`. :complexity: @@ -361,6 +374,7 @@ greater than :math:`n`, which is the degree of the bipartition. :returns: Whether or not the given block is transverse. :rtype: bool )pbdoc"); + thing.def("number_of_blocks", &Bipartition::number_of_blocks, R"pbdoc( @@ -376,6 +390,7 @@ number of parts in the partition that instances of this class represent. :rtype: int )pbdoc"); + thing.def("number_of_left_blocks", &Bipartition::number_of_left_blocks, R"pbdoc( @@ -393,6 +408,7 @@ of blocks in this partition. :rtype: int )pbdoc"); + // We call the no_checks variant here because x cannot (or at least should // not) be invalid thing.def( @@ -412,6 +428,7 @@ function returns a :any:`Blocks` object representing this partition. :complexity: :math:`O(n)` where :math:`n` is :any:`degree()`. )pbdoc"); + thing.def( "right_blocks", [](Bipartition const& x) { @@ -429,6 +446,7 @@ function returns a :any:`Blocks` object representing this partition. :complexity: :math:`O(n)` where :math:`n` is :any:`degree()`. )pbdoc"); + thing.def("number_of_right_blocks", &Bipartition::number_of_right_blocks, R"pbdoc( @@ -463,6 +481,7 @@ are referred to as the *transverse* blocks. :rtype: int )pbdoc"); + thing.def_static("one", &Bipartition::one, py::arg("n"), @@ -480,6 +499,7 @@ equal to ``n``. :returns: A newly constructed :any:`Bipartition`. :rtype: Bipartition )pbdoc"); + thing.def("lookup", &Bipartition::lookup, R"pbdoc( @@ -517,6 +537,7 @@ bipartition of degree equal to the degree of ``self``. :returns: A newly constructed :any:`Bipartition`. :rtype: Bipartition )pbdoc"); + m.def("bipartition_underlying_partition", &bipartition::underlying_partition, py::arg("x"), diff --git a/src/bmat8.cpp b/src/bmat8.cpp index 6dbaebb4a..8103b3b6d 100644 --- a/src/bmat8.cpp +++ b/src/bmat8.cpp @@ -35,16 +35,16 @@ namespace libsemigroups { namespace py = pybind11; void init_bmat8(py::module& m) { - py::class_ thing2(m, - "BMat8", - R"pbdoc( + py::class_ thing(m, + "BMat8", + R"pbdoc( Fast boolean matrices of dimension up to 8 x 8. Instance of this class represent 8 x 8 matrices over the boolean semiring. The -functions for these small matrices over the boolean semiring are more optimised -than the generic functions for boolean matrices. Note that all :any:`BMat8` are -represented internally as an 8 x 8 matrix; any entries not defined by the user -are taken to be ``0``. This does not affect the results of any calculations. +functions for these small matrices are more optimised than the generic +functions for boolean matrices. Note that all :any:`BMat8` are represented +internally as an 8 x 8 matrix; any entries not defined by the user are taken to +be ``0``. This does not affect the results of any calculations. There are numerous functions for computing things about :any:`BMat8` objects in the submodule ``bmat8``. @@ -123,44 +123,44 @@ the submodule ``bmat8``. // _libsemigroups_pybind11.StaticPerm16) -> int // 2. (self: _libsemigroups_pybind11.FroidurePinBase, w: list[int]) -> int // 3. (self: _libsemigroups_pybind11.FroidurePinBase, i: int) -> int - thing2.def("__len__", [](BMat8 const& x) { return 8; }); - thing2.def("__repr__", - [](BMat8 const& x) { return to_human_readable_repr(x, "[]"); }); - thing2.def( + thing.def("__len__", [](BMat8 const& x) { return 8; }); + thing.def("__repr__", + [](BMat8 const& x) { return to_human_readable_repr(x, "[]"); }); + thing.def( "__setitem__", [](BMat8& x, std::pair tup, bool val) { x.at(tup.first, tup.second) = val; }, py::is_operator()); - thing2.def( + thing.def( "__getitem__", [](BMat8 const& x, std::pair tup) { return x.at(tup.first, tup.second); }, py::is_operator()); - thing2.def( + thing.def( "__getitem__", [](BMat8 const& x, size_t r) { return bmat8::to_vector(x.at(r)); }, py::is_operator()); - thing2.def( + thing.def( "__hash__", [](BMat8 const& x) { return Hash()(x); }, py::is_operator()); - thing2.def(py::self == py::self); - thing2.def(py::self != py::self); - thing2.def(py::self <= py::self); - thing2.def(py::self >= py::self); - - thing2.def(py::self += py::self); - thing2.def(py::self + py::self); - thing2.def(py::self * bool()); - thing2.def(bool() * py::self); - thing2.def(py::self < py::self); - thing2.def(py::self > py::self); - thing2.def(py::self * py::self); - thing2.def(py::self *= py::self); - - thing2.def(py::init<>(), R"pbdoc( + thing.def(py::self == py::self); + thing.def(py::self != py::self); + thing.def(py::self <= py::self); + thing.def(py::self >= py::self); + + thing.def(py::self += py::self); + thing.def(py::self + py::self); + thing.def(py::self * bool()); + thing.def(bool() * py::self); + thing.def(py::self < py::self); + thing.def(py::self > py::self); + thing.def(py::self * py::self); + thing.def(py::self *= py::self); + + thing.def(py::init<>(), R"pbdoc( Default constructor. There is no guarantee about the contents of the matrix constructed. @@ -168,9 +168,9 @@ There is no guarantee about the contents of the matrix constructed. :complexity: Constant. )pbdoc"); - thing2.def(py::init(), - py::arg("val"), - R"pbdoc( + thing.def(py::init(), + py::arg("val"), + R"pbdoc( Construct from ``int``. This constructor initializes a :any:`BMat8` to have rows equal to the 8 chunks, @@ -180,9 +180,9 @@ of 8 bits each, of the binary representation of ``mat``. :type val: int :complexity: Constant.)pbdoc"); - thing2.def(py::init> const&>(), - py::arg("rows"), - R"pbdoc( + thing.def(py::init> const&>(), + py::arg("rows"), + R"pbdoc( Construct from list of rows. This constructor initializes a matrix where the rows of the matrix are the @@ -196,9 +196,9 @@ lists in ``rows``. :raises LibsemigroupsError: if the rows of *rows* are not all of the same length. :complexity: Constant.)pbdoc"); - thing2.def("degree", [](BMat8 const& self) { return 8; }); + thing.def("degree", [](BMat8 const& self) { return 8; }); - thing2.def( + thing.def( "copy", [](BMat8 const& self) { return BMat8(self); }, R"pbdoc( @@ -207,11 +207,11 @@ Copy a BMat8. :returns: A copy of the argument. :rtype: BMat8 )pbdoc"); - thing2.def("__copy__", [](BMat8 const& self) { return BMat8(self); }); + thing.def("__copy__", [](BMat8 const& self) { return BMat8(self); }); - thing2.def("to_int", - &BMat8::to_int, - R"pbdoc( + thing.def("to_int", + &BMat8::to_int, + R"pbdoc( Returns the integer representation of a :any:`BMat8`. Returns a non-negative integer obtained by interpreting an 8 x 8 :any:`BMat8` @@ -236,13 +236,14 @@ then realising this sequence as an unsigned int. >>> bin(x.to_int()) '0b100000010000000000000000000000000000000000000000000000000000000' )pbdoc"); - thing2.def("swap", - &BMat8::swap, - py::arg("that"), - R"pbdoc( -Swaps ``self`` with ``that``. -This function swaps the values of ``self`` and ``that``. + thing.def("swap", + &BMat8::swap, + py::arg("that"), + R"pbdoc( +Swaps *self* with *that*. + +This function swaps the values of *self* and *that*. :param that: the :any:`BMat8` to swap this with. :type that: BMat8 @@ -279,8 +280,10 @@ main diagonal equal to ``1`` and every other value equal to ``0``. :param dim: the dimension of the identity (default: 8) :type dim: int + :returns: A :any:`BMat8`. :rtype: BMat8 + :complexity: Constant. .. doctest:: @@ -292,6 +295,7 @@ main diagonal equal to ``1`` and every other value equal to ``0``. [0, 0, 1, 0], [0, 0, 0, 1]]) )pbdoc"); + m.def( "bmat8_random", [](size_t dim) { return bmat8::random(dim); }, @@ -308,18 +312,19 @@ This function returns a :any:`BMat8` chosen at random, where only the top-left :returns: A :any:`BMat8`. :rtype: BMat8 )pbdoc"); + m.def("bmat8_transpose", &bmat8::transpose, py::arg("x"), R"pbdoc( Returns the transpose of a :any:`BMat8`. -This function returns the transpose of its argument ``x`` , which is computed +This function returns the transpose of its argument *x* , which is computed using the technique found in :cite:`Knuth2009aa`. - :param x: the matrix to transpose. :type x: BMat8 + :returns: A :any:`BMat8`. :rtype: BMat8 @@ -334,6 +339,7 @@ using the technique found in :cite:`Knuth2009aa`. [0, 1, 0], [1, 0, 0]]) )pbdoc"); + m.def("bmat8_row_space_basis", &bmat8::row_space_basis, py::arg("x"), @@ -341,10 +347,11 @@ using the technique found in :cite:`Knuth2009aa`. Find a basis for the row space of a :any:`BMat8`. This function returns a :any:`BMat8` whose non-zero rows form a basis for the -row space of ``x``. +row space of *x*. :param x: the matrix. :type x: BMat8 + :returns: A :any:`BMat8`. :rtype: BMat8 @@ -366,10 +373,11 @@ row space of ``x``. Find a basis for the column space of a :any:`BMat8`. This function returns a :any:`BMat8` whose non-zero columns form a basis for -the column space of ``x``. +the column space of *x*. :param x: the matrix. :type x: BMat8 + :returns: A :any:`BMat8`. :rtype: BMat8 @@ -383,15 +391,16 @@ the column space of ``x``. BMat8([[1, 0], [0, 1]]) )pbdoc"); + m.def("bmat8_number_of_rows", &bmat8::number_of_rows, py::arg("x"), R"pbdoc( Returns the number of non-zero rows in a :any:`BMat8`. -:any:`BMat8` objects do not know their "dimension" - in effect they are all of dimension 8. -However, this function can be used to obtain the number of non-zero rows of a -:any:`BMat8`. +:any:`BMat8` objects do not know their "dimension" - in effect they are all of +dimension 8. However, this function can be used to obtain the number of +non-zero rows of a :any:`BMat8`. :param x: the matrix. :type x: BMat8 @@ -409,18 +418,20 @@ However, this function can be used to obtain the number of non-zero rows of a >>> bmat8.number_of_rows(x) 2 )pbdoc"); + m.def("bmat8_number_of_cols", &bmat8::number_of_cols, py::arg("x"), R"pbdoc( Returns the number of non-zero columns in a :any:`BMat8`. -:any:`BMat8` objects do not know their "dimension" - in effect they are all of dimension 8. -However, this function can be used to obtain the number of non-zero rows of a -:any:`BMat8`. +:any:`BMat8` objects do not know their "dimension" - in effect they are all of +dimension 8. However, this function can be used to obtain the number of +non-zero rows of a :any:`BMat8`. :param x: the matrix. :type x: BMat8 + :returns: The number of non-zero columns. :rtype: int @@ -435,6 +446,7 @@ However, this function can be used to obtain the number of non-zero rows of a >>> bmat8.number_of_cols(x) 3 )pbdoc"); + m.def("bmat8_row_space_size", &bmat8::row_space_size, py::arg("x"), @@ -459,6 +471,7 @@ Returns the size of the row space of a :any:`BMat8`. >>> bmat8.row_space_size(x) 6 )pbdoc"); + m.def("bmat8_col_space_size", &bmat8::col_space_size, py::arg("x"), @@ -468,7 +481,7 @@ Returns the size of the column space of a :any:`BMat8`. :param x: the matrix. :type x: BMat8 -:returns: The size of the column space of ``x``. +:returns: The size of the column space of *x*. :rtype: int :complexity: :math:`O(n)` where :math:`n` is the return value of this function. @@ -488,14 +501,14 @@ Returns the size of the column space of a :any:`BMat8`. R"pbdoc( Returns the minimum dimension of a :any:`BMat8`. -This function returns the maximal ``n`` such that row ``n`` or column ``n`` -contains a ``1``. Equivalent to the maximum of :any:`number_of_rows` and -:any:`number_of_cols`. +This function returns the maximal ``n`` such that row ``n`` or column ``n`` in +the boolean matrix *x* contains a ``1``. Equivalent to the maximum of +:any:`number_of_rows` and :any:`number_of_cols`. :param x: the matrix. :type x: BMat8 -:returns: The minimum dimension of **x** +:returns: The minimum dimension of *x*. :rtype: int :complexity: Constant. @@ -521,15 +534,15 @@ contains a ``1``. Equivalent to the maximum of :any:`number_of_rows` and R"pbdoc( Returns a list of the rows of a :any:`BMat8`. -This function returns the rows of ``x``. The returned list always has length 8, -even if ``x`` was constructed with fewer rows. +This function returns the rows of *x*. The returned list always has length 8, +even if *x* was constructed with fewer rows. :param x: the matrix. :type x: BMat8 :complexity: Constant. -:returns: The list of rows represented as integers. +:returns: The list of rows of the boolean matrix *x*. :rtype: list[list[bool]] .. doctest:: @@ -546,11 +559,12 @@ even if ``x`` was constructed with fewer rows. [False, False, False, False, False, False, False, False], [False, False, False, False, False, False, False, False]] )pbdoc"); + m.def("bmat8_is_regular_element", &bmat8::is_regular_element, py::arg("x"), R"pbdoc( -Check whether ``x`` is a regular element of the full boolean matrix monoid of +Check whether *x* is a regular element of the full boolean matrix monoid of appropriate dimension. :param x: the matrix. @@ -560,7 +574,7 @@ appropriate dimension. :returns: A ``True`` if there exists a boolean matrix ``y`` such that ``x * y * x = x`` - where ``x`` , and ``False`` otherwise. + where *x* , and ``False`` otherwise. :rtype: bool .. doctest:: diff --git a/src/dot.cpp b/src/dot.cpp index 3f61b73ba..8f46d19e4 100644 --- a/src/dot.cpp +++ b/src/dot.cpp @@ -52,12 +52,16 @@ render it with the `Graphviz `_ installation on your system. )pbdoc"); dot.def("__repr__", py::overload_cast(&to_human_readable_repr)); + + // This does not return by reference, not sure how to get it to, given that + // it returns a list of strings. dot.def_property_readonly( "colors", [](Dot const& self) { return Dot::colors; }, R"pbdoc( -An array of default HTML/hex colours. +A list of default HTML/hex colours. )pbdoc"); + dot.def(py::init<>(), R"pbdoc( Default constructor that constructs an empty :any:`Dot` object with no nodes, edges, attributes, or subgraphs. @@ -84,6 +88,7 @@ Copy a :any:`Dot` object. py::arg("name"), R"pbdoc( :sig=(self:Dot,name:str)->Dot.Node: + This function adds a node with name *name*. :param name: the name of the node to add. @@ -99,7 +104,7 @@ This function adds a node with name *name*. py::return_value_policy::reference_internal, // The return_value_policy ensures that the value is returned by // reference and that the Dot instance is kept alive until the - // last node reference is collected. + // last edge reference is collected. py::arg("head"), py::arg("tail"), R"pbdoc( @@ -116,11 +121,16 @@ This function adds an edge from the node named *head* to the node named *tail*. :raises LibsemigroupsError: if there is no node named *head* and/or *tail*. )pbdoc"); + dot.def( "add_subgraph", - [](Dot& self, Dot const& subgraph) { + [](Dot& self, Dot const& subgraph) -> Dot& { return self.add_subgraph(subgraph); }, + py::return_value_policy::reference_internal, + // The return_value_policy ensures that the value is returned by + // reference and that the Dot instance is kept alive until the + // last edge reference is collected. py::arg("subgraph"), R"pbdoc( This functions adds the :any:`Dot` object *subgraph* as a subgraph of ``self``. @@ -144,11 +154,12 @@ The following transformations are performed :returns: ``self``. :rtype: Dot )pbdoc"); + dot.def("edges", py::overload_cast<>(&Dot::edges, py::const_), R"pbdoc( :sig=(self:Dot)->list[Dot.Edge]: -Returns a list of the current edges (:any:`Edge` objects) in the +Returns a copy of the list of the current edges (:any:`Edge` objects) in the represented graph. :returns: @@ -160,7 +171,7 @@ represented graph. dot.def("subgraphs", py::overload_cast<>(&Dot::subgraphs, py::const_), R"pbdoc( -Returns a list of the current subgraphs (:any:`Dot` objects) in the +Returns a copy of the list of the current subgraphs (:any:`Dot` objects) in the represented graph. :returns: @@ -180,6 +191,7 @@ represented graph. :rtype: dict[str, str] )pbdoc"); + dot.def( "is_node", [](Dot const& self, std::string const& name) { @@ -229,6 +241,7 @@ Get the kind of the represented graph. :rtype: Dot.Kind )pbdoc"); + dot.def( "name", [](Dot const& self) { return self.name(); }, @@ -240,6 +253,7 @@ Get the current name of the represented graph. :rtype: str )pbdoc"); + dot.def( "name", [](Dot& self, std::string const& val) { return self.name(val); }, @@ -257,6 +271,7 @@ Set the name of the represented graph. :rtype: Dot )pbdoc"); + dot.def( "nodes", [](Dot const& d) -> std::vector { @@ -264,7 +279,7 @@ Set the name of the represented graph. }, R"pbdoc( :sig=(self: Dot) -> list[Dot.Node]: -Returns a list of the current nodes in the represented +Returns a copy of the list of the current nodes in the represented graph. :returns: @@ -272,17 +287,56 @@ graph. :rtype: list[Dot.Node] )pbdoc"); + dot.def( "node", [](Dot& d, std::string const& name) -> Dot::Node& { return d.node(name); }, - py::return_value_policy::reference_internal); // TODO(0) doc + py::return_value_policy::reference_internal, + py::arg("name"), + R"pbdoc( +:sig=(self: Dot, name: str) -> Dot.Node: +Returns the node object with name *name*. + +:param name: the name of the node. +:type name: str + +:returns: + The node named *name*. +:rtype: + Dot.Node + +:raises LibsemigroupsError: if there is no node named *name*. +)pbdoc"); + dot.def( "edge", [](Dot& d, std::string const& head, std::string const& tail) -> Dot::Edge& { return d.edge(head, tail); }, - py::return_value_policy::reference_internal); // TODO(0) doc + py::return_value_policy::reference_internal, + py::arg("head"), + py::arg("tail"), + R"pbdoc( +:sig=(self: Dot, head: str, tail: str) -> Dot.Edge: + +Returns the edge object with head named *head* and tail named *tail*. + +:param head: the name of the head node of the edge (node where the edge starts). +:type head: str + +:param tail: the name of the tail node of the edge (node where the edge ends). +:type tail: str + +:returns: + The edge from the node named *head* to the node named *tail*. +:rtype: + Dot.Edge + +:raises LibsemigroupsError: if there is no node named *head* or no node named *tail*. +:raises LibsemigroupsError: if there is no edge from *head* to *tail*. +)pbdoc"); + dot.def("__str__", &Dot::to_string); dot.def("to_string", &Dot::to_string, @@ -298,6 +352,7 @@ representation of the graph in the `DOT :rtype: str )pbdoc"); + dot.def("add_attr", &Dot::add_attr, py::arg("key"), @@ -333,18 +388,23 @@ attribute of an :any:`Dot`. )pbdoc", py::return_value_policy::reference); - py::enum_ kind(dot, "Kind", R"pbdoc( -TODO (although this doesn't seem to appear anywhere anyway). -)pbdoc"); - kind.value("digraph", Dot::Kind::digraph, R"pbdoc( -Value indicating that the represented graph has directed edges ->. -)pbdoc") - .value("graph", Dot::Kind::graph, R"pbdoc( -Value indicating that the represented graph has directed edges ->. -)pbdoc") - .value("subgraph", Dot::Kind::subgraph, R"pbdoc( -Value indicating that the represented graph has directed edges ->. -)pbdoc"); + py::enum_ kind( + dot, + "Kind", + R"pbdoc(Enum indicating what type of graph is represented by a :any:`Dot` object.)pbdoc"); + kind.value( + "digraph", + Dot::Kind::digraph, + R"pbdoc(Value indicating that the represented graph has directed edges ``->``.)pbdoc") + .value( + "graph", + Dot::Kind::graph, + R"pbdoc(Value indicating that the represented graph has (undirected) edges ``--``.)pbdoc") + .value( + "subgraph", + Dot::Kind::subgraph, + R"pbdoc(Value indicating that a :any:`Dot` object is a subgraph of another :any:`Dot` object.)pbdoc"); + kind.def("__repr__", [](Dot::Kind const& knd) { return to_human_readable_repr(knd, "."); }); diff --git a/src/forest.cpp b/src/forest.cpp index c793f03e2..011df21d8 100644 --- a/src/forest.cpp +++ b/src/forest.cpp @@ -98,6 +98,7 @@ Construct a :any:`Forest` from list of *parents* and *labels*. :any:`set_parent_and_label` throws for ``parent[i]`` and ``edge_labels[i]`` for any value of ``i``. )pbdoc"); + thing.def("add_nodes", &Forest::add_nodes, py::arg("n"), @@ -111,6 +112,7 @@ This function adds *n* nodes to the forest, but no edges. :complexity: At most linear in ``number_of_nodes() + n``. )pbdoc"); + thing.def("empty", &Forest::empty, R"pbdoc( @@ -125,9 +127,10 @@ Check if there are any nodes in the forest. This function returns :complexity: Constant. )pbdoc"); + thing.def("init", &Forest::init, - py::arg("n"), + py::arg("n") = 0, R"pbdoc( Reinitialize an existing :any:`Forest` object. @@ -140,6 +143,7 @@ the same state as if it had just be constructed as ``Forest(n)``. :returns: ``self``. :rtype: Forest )pbdoc"); + thing.def( "label", [](Forest const& self, @@ -171,6 +175,7 @@ Returns the label of the edge from a node to its parent. :complexity: Constant. )pbdoc"); + thing.def( "labels", [](Forest const& self) @@ -188,7 +193,7 @@ Returns the label of the edge from a node to its parent. R"pbdoc( :sig=(self: Forest) -> list[int | Undefined]: -Returns the list of edge labels in the :any:`Forest`. The value +Returns a copy of the list of edge labels in the :any:`Forest`. The value in position ``i`` of this list is the label of the edge from the parent of node ``i`` to ``i``. If the parent equals :any:`UNDEFINED`, then node ``i`` is a root node. @@ -201,6 +206,7 @@ then node ``i`` is a root node. :complexity: Constant. )pbdoc"); + thing.def("number_of_nodes", &Forest::number_of_nodes, R"pbdoc( @@ -215,8 +221,10 @@ in the forest. :complexity: Constant. )pbdoc"); + thing.def(py::self != py::self, py::arg("that")); thing.def(py::self == py::self, py::arg("that")); + thing.def( "parent", [](Forest const& self, @@ -226,6 +234,7 @@ in the forest. py::arg("i"), R"pbdoc( :sig=(self: Forest, i: int) -> int | Undefined: + Returns the parent of a node. :param i: @@ -244,6 +253,7 @@ Returns the parent of a node. :complexity: Constant )pbdoc"); + thing.def( "parents", [](Forest const& self) @@ -274,6 +284,7 @@ then node ``i`` is a root node. :complexity: Constant. )pbdoc"); + thing.def( "path_to_root", [](Forest const& self, node_type i) { @@ -293,6 +304,7 @@ to *i*. :raises LibsemigroupsError: if *i* is greater than or equal to :any:`number_of_nodes`. )pbdoc"); + thing.def("set_parent_and_label", &Forest::set_parent_and_label, py::arg("node"), diff --git a/src/froidure-pin-base.cpp b/src/froidure-pin-base.cpp index a30498646..f6bf15b2e 100644 --- a/src/froidure-pin-base.cpp +++ b/src/froidure-pin-base.cpp @@ -117,7 +117,7 @@ generators) equal to the element with index *pos*. :rtype: int :raises LibsemigroupsError: - if ``pos`` is greater than or equal to :any:`current_size`. + if *pos* is greater than or equal to :any:`current_size`. :complexity: Constant. @@ -159,17 +159,19 @@ defining the semigroup if the semigroup is fully enumerated. :complexity: Constant. )pbdoc"); + thing.def("current_right_cayley_graph", &FroidurePinBase::current_right_cayley_graph, R"pbdoc( :sig=(self: FroidurePin) -> WordGraph: -Returns a const reference to the right Cayley graph. This function -triggers a full enumeration, and then returns the right Cayley graph of -the semigroup represented by a :any:`FroidurePin` instance. +Returns the so-far enumerated right Cayley graph. + +This function return the right Cayley graph of the semigroup as it has been +enumerated so-far. No enumeration is triggered by calls to this function. :returns: - The full enumerated right Cayley graph. + The (possibly partially enumerated) left Cayley graph. :rtype: WordGraph @@ -384,6 +386,7 @@ far with length *len*. This function does not trigger any enumeration. :complexity: Constant. )pbdoc"); + thing.def( "number_of_elements_of_length", [](FroidurePinBase const& self, size_t min, size_t max) { @@ -429,6 +432,7 @@ This function triggers a full enumeration of the semigroup. ``self`` , and :math:`n` is the return value of :any:`FroidurePin.number_of_generators`. )pbdoc"); + thing.def("prefix", &FroidurePinBase::prefix, py::arg("pos"), diff --git a/src/froidure-pin.cpp b/src/froidure-pin.cpp index fd8c318c7..d0962cfa1 100644 --- a/src/froidure-pin.cpp +++ b/src/froidure-pin.cpp @@ -102,6 +102,7 @@ Calling this function does not trigger any enumeration. :returns: An iterator yielding the so-far enumerated elements. :rtype: Iterator[Element] )pbdoc"); + thing.def(py::init([](std::vector const& gens) { return make(gens); }), @@ -119,8 +120,10 @@ in the list *gens*. :raises LibsemigroupsError: if the generators do not all have the same degree. )pbdoc"); + thing.def("__copy__", [](FroidurePin_ const& self) { return FroidurePin_(self); }); + thing.def( "copy", [](FroidurePin_ const& self) { return FroidurePin_(self); }, @@ -132,10 +135,12 @@ Copy a :any:`FroidurePin` object. :returns: A copy. :rtype: FroidurePin )pbdoc"); + // This function should really throw a ValueError if the degree of x is // incompatible with the existing degree, but this doesn't get detected at // the Python level, so a LibsemigroupsError is thrown instead. It would // be possible to intercept this, but it probably isn't worth the effort. + thing.def("add_generator", &FroidurePin_::add_generator, py::arg("x"), @@ -176,6 +181,7 @@ elements than before (whether it is fully enumerating or not). :raises TypeError: if *x* is not of the same type as the existing generators (if any). )pbdoc"); + thing.def( "add_generators", [](FroidurePin_& self, @@ -256,6 +262,7 @@ the next idempotent. :rtype: Iterator[Element] )pbdoc"); + thing.def( "position_of_generator", [](FroidurePinBase const& self, size_t i) { @@ -285,6 +292,7 @@ return *i*, examples of when this will not be the case are: :complexity: Constant. )pbdoc"); + thing.def( "sorted_elements", [](FroidurePin_& self) { @@ -337,6 +345,7 @@ or number of idempotents, for example. :raises LibsemigroupsError: if the elements in *gens* do not all have the same degree. )pbdoc"); + thing.def("contains", &FroidurePin_::contains, py::arg("x"), @@ -356,6 +365,7 @@ instance and ``False`` if it does not. instance. :rtype: bool )pbdoc"); + thing.def( "copy_add_generators", [](FroidurePin_& self, @@ -464,10 +474,14 @@ better to just multiply the transformations together. :any:`FroidurePin.current_size`. )pbdoc"); - thing.def("generator", - &FroidurePin_::generator, - py::arg("i"), - R"pbdoc( + thing.def( + "generator", + [](FroidurePin_ const& self, size_t i) -> Element const& { + return self.generator(i); + }, + py::return_value_policy::reference_internal, + py::arg("i"), + R"pbdoc( :sig=(self: FroidurePin, i: int) -> Element: Returns the generator with specified index. @@ -504,7 +518,8 @@ the same state as if it had just been default constructed. thing.def( "init", - [](FroidurePin_& self, std::vector const& gens) { + [](FroidurePin_& self, + std::vector const& gens) -> FroidurePin_& { FroidurePin_::throw_if_inconsistent_degree(gens.cbegin(), gens.cend()); return froidure_pin::init(self, gens); @@ -644,10 +659,14 @@ the :any:`Runner.run` function, and consequently every other function too. :rtype: FroidurePin )pbdoc"); - thing.def("sorted_at", - &FroidurePin_::sorted_at, - py::arg("i"), - R"pbdoc( + thing.def( + "sorted_at", + [](FroidurePin_& self, size_t i) -> Element const& { + return self.sorted_at(i); + }, + py::return_value_policy::reference_internal, + py::arg("i"), + R"pbdoc( :sig=(self: FroidurePin, i: int) -> Element: Access element specified by sorted index with bound checks. @@ -782,9 +801,7 @@ element of *fp* and ``False`` otherwise. :any:`FroidurePin.number_of_generators`. .. note:: - No enumeration of *fp* is triggered by calls to this function. - - )pbdoc"); + No enumeration of *fp* is triggered by calls to this function.)pbdoc"); m.def( "froidure_pin_factorisation", @@ -794,7 +811,7 @@ element of *fp* and ``False`` otherwise. py::arg("fp"), py::arg("x"), R"pbdoc( -:sig=(fp: FroidurePin, x: Union[Element, int]) -> list[int]: +:sig=(fp: FroidurePin, x: Element | int) -> list[int]: :only-document-once: Returns a word containing a factorisation (in the generators) of an @@ -837,7 +854,7 @@ is that the resulting factorisation may not be minimal. py::arg("fp"), py::arg("x"), R"pbdoc( -:sig=(fp: FroidurePin, x: Union[Element, int]) -> list[int]: +:sig=(fp: FroidurePin, x: Element | int) -> list[int]: :only-document-once: Returns a word containing a minimal factorisation (in the generators) @@ -879,7 +896,7 @@ that evaluates to *x*. py::arg("fp"), py::arg("w"), R"pbdoc( -:sig=(fp: FroidurePin, x: list[int]) -> int: +:sig=(fp: FroidurePin, w: list[int]) -> int: :only-document-once: Returns the position corresponding to a word. @@ -904,9 +921,10 @@ full enumeration is triggered by calls to this function. m.def( "froidure_pin_to_element", - [](FroidurePin_& fp, word_type const& w) { + [](FroidurePin_& fp, word_type const& w) -> Element const& { return froidure_pin::to_element(fp, w); }, + py::return_value_policy::reference_internal, py::arg("fp"), py::arg("w").noconvert(), R"pbdoc( @@ -935,7 +953,6 @@ This function returns the element of *fp* obtained by evaluating *w*. .. note:: No enumeration of *fp* is triggered by calls to this function.)pbdoc"); } - // TODO(1) are there some functions missing here? } // bind_froidure_pin } // namespace diff --git a/src/gabow.cpp b/src/gabow.cpp index 1a87f166a..17f59627c 100644 --- a/src/gabow.cpp +++ b/src/gabow.cpp @@ -65,9 +65,12 @@ by a relevant member function. The complexity of Gabow's algorithm is at most :math:`O(mn)` where ``m`` is :any:`WordGraph.number_of_nodes()` and ``n`` is :any:`WordGraph.out_degree()`. )pbdoc"); + thing.def("__repr__", [](Gabow_ const& g) { return to_human_readable_repr(g); }); + thing.def("__copy__", [](Gabow_ const& g) { return Gabow_(g); }); + thing.def( "copy", [](Gabow_ const& self) { return Gabow_(self); }, @@ -79,15 +82,16 @@ Copy a :any:`Gabow` object. :returns: A copy. :rtype: Gabow )pbdoc"); + thing.def(py::init const&>(), py::arg("wg"), R"pbdoc( This function constructs a :any:`Gabow` object from the :any:`WordGraph` *wg*. )pbdoc"); + thing.def("component", &Gabow_::component, py::arg("i"), - py::return_value_policy::reference_internal, R"pbdoc( Returns a list containing the strongly connected component with given index. @@ -111,10 +115,10 @@ to construct the :any:`Gabow` instance. .. seealso:: :any:`component_of` to obtain the :any:`component` of a node. )pbdoc"); + thing.def("component_of", &Gabow_::component_of, py::arg("n"), - py::return_value_policy::reference_internal, R"pbdoc( Returns a list containing the strongly connected component of a given node. @@ -135,9 +139,9 @@ construct the :any:`Gabow` instance. This function triggers the computation of the strongly connected components (if they are not already known). )pbdoc"); + thing.def("components", &Gabow_::components, - py::return_value_policy::reference_internal, R"pbdoc( This function returns a list of lists containing all of the strongly connected components of the :any:`WordGraph` (returned by :any:`Gabow.word_graph`) used to @@ -152,6 +156,7 @@ construct the :any:`Gabow` instance. This function triggers the computation of the strongly connected components (if they are not already known). )pbdoc"); + thing.def("has_components", &Gabow_::has_components, R"pbdoc( @@ -163,11 +168,8 @@ function returns ``True`` if the strongly connected components of a Whether or not the strongly connected components have been found already. :rtype: bool - -.. note:: - This function triggers the computation of the strongly connected - components (if they are not already known). )pbdoc"); + thing.def("id", &Gabow_::id, py::arg("n"), @@ -189,6 +191,7 @@ underlying graph of a :any:`Gabow` instance. This function triggers the computation of the strongly connected components (if they are not already known). )pbdoc"); + thing.def("init", &Gabow_::init, py::arg("wg"), @@ -199,6 +202,7 @@ state as if it had just been constructed from *wg*. :returns: ``self``. :rtype: Gabow )pbdoc"); + thing.def("number_of_components", &Gabow_::number_of_components, R"pbdoc( @@ -214,6 +218,7 @@ underlying :any:`WordGraph` (returned by :any:`Gabow.word_graph`). This function triggers the computation of the strongly connected components (if they are not already known). )pbdoc"); + thing.def("reverse_spanning_forest", &Gabow_::reverse_spanning_forest, py::return_value_policy::reference_internal, @@ -232,6 +237,7 @@ root. This function triggers the computation of the strongly connected components (if they are not already known). )pbdoc"); + thing.def("root_of", &Gabow_::root_of, py::arg("n"), @@ -259,6 +265,7 @@ root_of(b)``. This function triggers the computation of the strongly connected components (if they are not already known). )pbdoc"); + thing.def( "roots", [](Gabow_& self) { @@ -280,6 +287,7 @@ components of the underlying word graph. This function triggers the computation of the strongly connected components (if they are not already known). )pbdoc"); + thing.def("spanning_forest", &Gabow_::spanning_forest, py::return_value_policy::reference_internal, @@ -298,6 +306,7 @@ minimum node of that component, with edges oriented away from the root. This function triggers the computation of the strongly connected components (if they are not already known). )pbdoc"); + thing.def("word_graph", &Gabow_::word_graph, py::return_value_policy::reference_internal, diff --git a/src/kambites.cpp b/src/kambites.cpp index e05374fe9..a546904b6 100644 --- a/src/kambites.cpp +++ b/src/kambites.cpp @@ -44,7 +44,7 @@ namespace libsemigroups { py::class_ thing(m, name.c_str(), R"pbdoc( -Class template implementing small overlap class, equality, and normal forms for +Class implementing small overlap class, equality, and normal forms for small overlap monoids. This page describes the class :any:`Kambites` for determining the @@ -170,8 +170,10 @@ at least :math:`n`. thing.def("ukkonen", &Kambites_::ukkonen, + py::return_value_policy::reference_internal, R"pbdoc( :sig=(self: Kambites) -> Ukkonen: + Returns the generalised suffix tree used to compute pieces. This function returns the generalised suffix tree :any:`Ukkonen` object diff --git a/src/knuth-bendix-impl.cpp b/src/knuth-bendix-impl.cpp index 529973183..f4309b59f 100644 --- a/src/knuth-bendix-impl.cpp +++ b/src/knuth-bendix-impl.cpp @@ -55,7 +55,7 @@ namespace libsemigroups { ////////////////////////////////////////////////////////////////////////// py::class_, detail::CongruenceCommon> thing( - m, name.c_str(), R"pbdoc()pbdoc"); + m, name.c_str()); ////////////////////////////////////////////////////////////////////////// // KnuthBendixImpl nested classes . . . @@ -139,7 +139,7 @@ accumulate. &KnuthBendixImpl::max_pending_rules), py::arg("val"), R"pbdoc( -:sig=(self: KnuthBendix) -> int: +:sig=(self: KnuthBendix) -> KnuthBendix: Specify the number of pending rules that must accumulate before they are reduced, processed, and added to the system. @@ -177,12 +177,16 @@ confluence. .. seealso:: :any:`Runner.run`. )pbdoc"); - thing.def("check_confluence_interval", - py::overload_cast( - &KnuthBendixImpl::check_confluence_interval), - py::arg("val"), - R"pbdoc( -:sig=(self: KnuthBendix) -> KnuthBendix: + thing.def( + "check_confluence_interval", + [](KnuthBendixImpl& self, + int_or_constant const& val) -> KnuthBendixImpl& { + return self.check_confluence_interval(to_int(val)); + }, + py::return_value_policy::reference_internal, + py::arg("val"), + R"pbdoc( +:sig=(self: KnuthBendix, val: int | LimitMax) -> KnuthBendix: Set the interval at which confluence is checked. @@ -197,7 +201,7 @@ The default value is ``4096``, and should be set to system is already confluent. :param val: The new value of the interval. -:type val: int +:type val: int | LimitMax :return: ``self``. :rtype: KnuthBendix @@ -224,9 +228,11 @@ of rules that should be considered in :any:`Runner.run`. thing.def( "max_overlap", - [](KnuthBendixImpl& self, int_or_constant val) { + [](KnuthBendixImpl& self, + int_or_constant val) -> KnuthBendixImpl& { return self.max_overlap(to_int(val)); }, + py::return_value_policy::reference_internal, py::arg("val"), R"pbdoc( :sig=(self: KnuthBendix, val: int | PositiveInfinity) -> KnuthBendix: @@ -271,9 +277,11 @@ system may not be confluent. thing.def( "max_rules", - [](KnuthBendixImpl& self, int_or_constant val) { + [](KnuthBendixImpl& self, + int_or_constant val) -> KnuthBendixImpl& { return self.max_rules(to_int(val)); }, + py::return_value_policy::reference_internal, py::arg("val"), R"pbdoc( :sig=(self: KnuthBendix, val: int | PositiveInfinity) -> KnuthBendix: @@ -434,7 +442,6 @@ Check if the current system knows the state of confluence of the current rules. [](KnuthBendixImpl& kb) -> WordGraph const& { return kb.gilman_graph(); }, - // REVIEW: Should WordGraph be formatted as code, or as text? R"pbdoc( :sig=(self: KnuthBendix) -> WordGraph: diff --git a/src/knuth-bendix.cpp b/src/knuth-bendix.cpp index a5f453579..aa5906be3 100644 --- a/src/knuth-bendix.cpp +++ b/src/knuth-bendix.cpp @@ -331,21 +331,16 @@ redundant in this way, then ``None`` is returned. ('bab', 'abb') )pbdoc"); - m.def( - "knuth_bendix_redundant_rule", - [](Presentation const& p, std::chrono::milliseconds t) - -> std::optional> { - auto it = knuth_bendix::redundant_rule(p, t); - if (it != p.rules.cend()) { - return std::make_pair(*it, *(it + 1)); - } - return {}; - }, - R"pbdoc( -:sig=(p: Presentation, t: datetime.timedelta) -> tuple[list[int], list[int]] | tuple[str, str] | None: -:only-document-once: -TODO where's the doc? -)pbdoc"); + // Documented above + m.def("knuth_bendix_redundant_rule", + [](Presentation const& p, std::chrono::milliseconds t) + -> std::optional> { + auto it = knuth_bendix::redundant_rule(p, t); + if (it != p.rules.cend()) { + return std::make_pair(*it, *(it + 1)); + } + return {}; + }); } // bind_knuth_bendix template diff --git a/src/konieczny.cpp b/src/konieczny.cpp index 85a984b88..ae826a40c 100644 --- a/src/konieczny.cpp +++ b/src/konieczny.cpp @@ -72,16 +72,8 @@ Copy a Konieczny. :rtype: Konieczny )pbdoc"); - thing.def(py::init<>(), R"pbdoc( -:sig=(self: Konieczny) -> None: - -Default constructor. - -This is the standard constructor for a :any:`Konieczny` instance with -unspecified generators. - -.. seealso:: - :any:`Konieczny.add_generator` and :any:`Konieczny.add_generators`.)pbdoc"); + // This constructor can't be used directly so isn't documented. + thing.def(py::init<>()); thing.def(py::init([](std::vector const& gens) { return make(gens); @@ -120,6 +112,9 @@ times. :param x: the generator to add. :type x: Element +:returns: ``self``. +:rtype: Konieczny + :raises LibsemigroupsError: if the degree of *x* is incompatible with the existing degree. :raises LibsemigroupsError: if :any:`started` returns ``True``. @@ -127,12 +122,14 @@ times. thing.def( "add_generators", - [](Konieczny_& self, std::vector const& coll) { - return konieczny::add_generators(self, coll); + [](Konieczny_& self, + std::vector const& coll) -> Konieczny_& { + konieczny::add_generators(self, coll); + return self; }, py::arg("coll"), R"pbdoc( -:sig=(self: Konieczny, coll: list[Element]) -> None: +:sig=(self: Konieczny, coll: list[Element]) -> Konieczny: Add collection of generators from a list. @@ -141,11 +138,14 @@ See :any:`Konieczny.add_generator` for a detailed description. :param coll: the collection of generators to add. :type coll: list[Element] +:returns: ``self``. +:rtype: Konieczny + :raises LibsemigroupsError: the degree of any item in *coll* is incompatible with the existing degree (if any). :raises LibsemigroupsError: - :any:`Runner.started` returns ``True``. + :any:`started` returns ``True``. )pbdoc"); thing.def( "current_D_classes", @@ -394,6 +394,7 @@ the same degree; this function returns that degree. )pbdoc"); thing.def("generator", &Konieczny_::generator, + py::return_value_policy::reference_internal, py::arg("pos"), R"pbdoc( :sig=(self: Konieczny, pos: int) -> Element: @@ -671,6 +672,7 @@ not already known. )pbdoc"); thing2.def("rep", &Konieczny_::DClass::rep, + py::return_value_policy::reference_internal, R"pbdoc( :sig=(self: Konieczny.DClass) -> Element: diff --git a/src/libsemigroups_pybind11/adapters.py b/src/libsemigroups_pybind11/adapters.py index 07673b4d9..8da36e623 100644 --- a/src/libsemigroups_pybind11/adapters.py +++ b/src/libsemigroups_pybind11/adapters.py @@ -68,7 +68,9 @@ def __init__(self: _Self, *args, point=None, element=None) -> None: if _to_cxx(self) is not None: return if len(args) != 0: - raise TypeError(f"expected 0 positional arguments, but found {len(args)}") + raise TypeError( + f"expected 0 positional arguments, but found {len(args)}" + ) self.py_template_params = ( type(_to_cxx(element)), type(_to_cxx(point)), diff --git a/src/libsemigroups_pybind11/aho_corasick.py b/src/libsemigroups_pybind11/aho_corasick.py index ca7048f07..58ceb758e 100644 --- a/src/libsemigroups_pybind11/aho_corasick.py +++ b/src/libsemigroups_pybind11/aho_corasick.py @@ -15,7 +15,7 @@ from _libsemigroups_pybind11 import ( # pylint: disable=no-name-in-module,unused-import aho_corasick_add_word as add_word, - rm_word, - traverse_word, + aho_corasick_rm_word as rm_word, + aho_corasick_traverse_word as traverse_word, aho_corasick_dot as dot, ) diff --git a/src/libsemigroups_pybind11/sims.py b/src/libsemigroups_pybind11/sims.py index 06792c49d..526f94b2f 100644 --- a/src/libsemigroups_pybind11/sims.py +++ b/src/libsemigroups_pybind11/sims.py @@ -82,7 +82,7 @@ class Sims1(_SimsBase): # pylint: disable=missing-class-docstring (list[int],): _Sims1, } - _cxx_to_py_type_dict = dict( + _cxx_type_to_py_template_params = dict( zip( _py_template_params_to_cxx_type.values(), _py_template_params_to_cxx_type.keys(), diff --git a/src/matrix.cpp b/src/matrix.cpp index fb887d0e2..7e24759fd 100644 --- a/src/matrix.cpp +++ b/src/matrix.cpp @@ -45,27 +45,31 @@ namespace libsemigroups { namespace py = pybind11; namespace { - template - Mat const* semiring(size_t threshold) { - static std::unordered_map> cache; + template + Semiring const* semiring(size_t threshold) { + static std::unordered_map> cache; auto it = cache.find(threshold); if (it == cache.end()) { - it = cache.emplace(threshold, std::make_unique(threshold)) + it = cache + .emplace(threshold, + std::make_unique(threshold)) .first; } return it->second.get(); } - template - Mat const* semiring(size_t threshold, size_t period) { + template + Semiring const* semiring(size_t threshold, size_t period) { static std::unordered_map, - std::unique_ptr, + std::unique_ptr, Hash>> cache; auto tp = std::make_pair(threshold, period); auto it = cache.find(tp); if (it == cache.end()) { - it = cache.emplace(tp, std::make_unique(threshold, period)) + it = cache + .emplace(tp, + std::make_unique(threshold, period)) .first; } return it->second.get(); diff --git a/src/paths.cpp b/src/paths.cpp index 69e2ba244..73350e3e2 100644 --- a/src/paths.cpp +++ b/src/paths.cpp @@ -17,13 +17,8 @@ // // C++ stl headers.... -#include // for array -#include // for uint32_t -#include // for uint64_t -#include // for initializer_list -#include // for string -#include // for to_string, basic_string -#include // for vector +#include // for uint32_t +#include // for uint64_t // libsemigroups.... #include // for operator!=, operator== diff --git a/src/present.cpp b/src/present.cpp index 74483af8e..54055bf86 100644 --- a/src/present.cpp +++ b/src/present.cpp @@ -17,18 +17,10 @@ // // C std headers.... -#include // for isprint -#include // for size_t -#include // for int32_t, uint32_t +#include // for size_t // C++ stl headers.... -#include // for for_each, none_of, search -#include // for pow -#include // for distance -#include // for string, basic_string, oper... -#include // for operator!=, operator== -#include // for move, swap -#include // for vector +#include // for string, basic_string, oper... // libsemigroups.... #include // for operator==, UNDEFINED @@ -62,13 +54,13 @@ namespace libsemigroups { py::class_ thing(m, name.c_str(), R"pbdoc( -For an implementation of presentations for semigroups or monoids. +Presentations for semigroups and monoids. This class can be used to construction presentations for semigroups or monoids and is intended to be used as the input to other algorithms in ``libsemigroups_pybind11``. The idea is to provide a shallow wrapper around a collection of words of type :ref:`Word`. We refer to -this vector of words as the rules of the presentation. The :any:`Presentation` +this vector of words as the *rules* of the presentation. The :any:`Presentation` class also provides some checks that the rules really define a presentation, (i.e. it's consistent with its alphabet), and some related functionality is available in the module :any:`libsemigroups_pybind11.presentation`.)pbdoc"); @@ -118,7 +110,9 @@ Return the alphabet of the presentation. )pbdoc"); thing.def( "alphabet", - [](Presentation_& self, size_type n) { return self.alphabet(n); }, + [](Presentation_& self, size_type n) -> Presentation_& { + return self.alphabet(n); + }, py::arg("n"), R"pbdoc( :sig=(self: Presentation, int: n) -> Presentation: @@ -145,7 +139,7 @@ order of letters to be a-zA-Z0-9. thing.def( "alphabet", [](Presentation_& self, - typename Presentation_::word_type const& lphbt) { + typename Presentation_::word_type const& lphbt) -> Presentation_& { return self.alphabet(lphbt); }, py::arg("lphbt"), @@ -209,7 +203,7 @@ but is not given as a quotient of a free monoid. )pbdoc"); thing.def( "contains_empty_word", - [](Presentation_& self, bool val) { + [](Presentation_& self, bool val) -> Presentation_& { return self.contains_empty_word(val); }, py::arg("val"), @@ -393,32 +387,36 @@ Add the first letter not in the alphabet as a generator, and return this letter. )pbdoc"); thing.def( "add_generator", - [](Presentation_& self, typename Presentation_::letter_type x) { - self.add_generator(x); - }, + [](Presentation_& self, typename Presentation_::letter_type x) + -> Presentation_& { return self.add_generator(x); }, py::arg("x"), R"pbdoc( -:sig=(self: Presentation, x: Letter) -> Letter: +:sig=(self: Presentation, x: Letter) -> Presentation: Add the letter *x* as a generator. :param x: the letter to add as a generator. :type x: :ref:`Letter` +:returns: ``self``. +:rtype: Presentation. + :raises LibsemigroupsError: if *x* is in ``alphabet()``.)pbdoc"); thing.def( "remove_generator", - [](Presentation_& self, typename Presentation_::letter_type x) { - self.remove_generator(x); - }, + [](Presentation_& self, typename Presentation_::letter_type x) + -> Presentation_& { return self.remove_generator(x); }, py::arg("x"), R"pbdoc( -:sig=(self: Presentation, x: Letter) -> None: +:sig=(self: Presentation, x: Letter) -> Presentation: Remove the letter *x* as a generator. :param x: the letter to remove as a generator. :type x: :ref:`Letter` +:returns: ``self``. +:rtype: Presentation. + :raises LibsemigroupsError: if *x* is not in `p.alphabet()`. :complexity: Average case: linear in the length of the alphabet, worst case: diff --git a/src/runner.cpp b/src/runner.cpp index b6de2819c..a35fb084f 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -200,7 +200,7 @@ Set the last report time point to now. )pbdoc"); thing.def( "report_prefix", - [](Reporter& self, std::string const& val) { + [](Reporter& self, std::string const& val) -> Reporter& { return self.report_prefix(val); }, py::arg("val"), diff --git a/src/schreier-sims.cpp b/src/schreier-sims.cpp index 410753f84..661e8b08a 100644 --- a/src/schreier-sims.cpp +++ b/src/schreier-sims.cpp @@ -95,7 +95,7 @@ Copy a :any:`SchreierSims`. &SchreierSims_::add_base_point, py::arg("pt"), R"pbdoc( -:sig=(self: SchreierSims, pt: int) -> None: +:sig=(self: SchreierSims, pt: int) -> SchreierSims: Add a base point to the stabiliser chain. @@ -223,6 +223,7 @@ Check if the stabiliser chain is fully enumerated. )pbdoc"); thing.def("generator", &SchreierSims_::generator, + py::return_value_policy::reference_internal, py::arg("index"), R"pbdoc( :sig=(self: SchreierSims, index: int) -> Element: @@ -258,6 +259,7 @@ represents the trivial group, as if ``self`` had been newly constructed. )pbdoc"); thing.def("inverse_transversal_element", &SchreierSims_::inverse_transversal_element, + py::return_value_policy::reference_internal, py::arg("depth"), py::arg("pt"), R"pbdoc( @@ -321,6 +323,7 @@ a given depth. )pbdoc"); thing.def("one", &SchreierSims_::one, + py::return_value_policy::reference_internal, R"pbdoc( :sig=(self: SchreierSims) -> Element: Returns a const reference to the identity. @@ -417,6 +420,7 @@ Returns the size of the group represented by this, without running the algorithm )pbdoc"); thing.def("strong_generator", &SchreierSims_::strong_generator, + py::return_value_policy::reference_internal, py::arg("depth"), py::arg("index"), R"pbdoc( @@ -443,6 +447,7 @@ This function returns the generator with a given depth and index. )pbdoc"); thing.def("transversal_element", &SchreierSims_::transversal_element, + py::return_value_policy::reference_internal, py::arg("depth"), py::arg("pt"), R"pbdoc( diff --git a/src/sims.cpp b/src/sims.cpp index 1f209e3d8..918884524 100644 --- a/src/sims.cpp +++ b/src/sims.cpp @@ -224,7 +224,7 @@ such rules. The relative orders of the rules within R"pbdoc( Get all active pruners of the search tree. -This function returns the list of pruners. A pruner is any function that takes +This function returns a copy of the list of pruners. A pruner is any function that takes as input a word graph and returns a boolean. We require that if a pruner returns ``False`` for a word graph ``wg``, then it returns ``False`` for all word graphs that are descended from ``wg`` in the Sims word graph search tree. @@ -236,7 +236,7 @@ by :py:meth:`~Sims1.pruners`. :returns: A list of boolean functions on word graphs, the set of all pruners. :rtype: list[Callable[[WordGraph], bool]] -)pbdoc", +)pbdoc", // The next line seemingly does nothing py::return_value_policy::reference_internal); ss.def( @@ -394,7 +394,7 @@ represented by the relations of the presentation returned by ss.def( "add_excluded_pair", [](SimsSettings_& self, word_type const& lhs, word_type const& rhs) - -> Subclass { return sims::add_excluded_pair(self, lhs, rhs); }, + -> Subclass& { return sims::add_excluded_pair(self, lhs, rhs); }, py::arg("lhs"), py::arg("rhs"), R"pbdoc( @@ -1099,8 +1099,8 @@ time being. py::class_> ro(m, "RepOrc", R"pbdoc( -For computing small degree transformation representations of a finite semigroup -or monoid. +For computing small degree transformation representations of a finite +semigroup or monoid. This class is a helper for :any:`Sims1` calling the :any:`word_graph` member function attempts to find a right congruence, represented as an @@ -1527,7 +1527,7 @@ monoid defined by *p*. )pbdoc"); sri.def( "init", - [](SimsRefinerIdeals& self) -> SimsRefinerIdeals { + [](SimsRefinerIdeals& self) -> SimsRefinerIdeals& { return self.init(); }, R"pbdoc( diff --git a/src/stephen.cpp b/src/stephen.cpp index e375e1043..6a679ba32 100644 --- a/src/stephen.cpp +++ b/src/stephen.cpp @@ -252,6 +252,7 @@ Returns the word set by :py:meth:`~Stephen.set_word`. )pbdoc"); thing.def("word_graph", &Stephen_::word_graph, + py::return_value_policy::reference_internal, R"pbdoc( :sig=(self: Stephen) -> WordGraph: @@ -268,6 +269,7 @@ implemented in this class is not triggered by calls to this function. with :py:meth:`~Stephen.init` or if no word was set with :py:meth:`~Stephen.set_word`. )pbdoc"); + thing.def_static("initial_state", &Stephen_::initial_state, R"pbdoc( diff --git a/src/todd-coxeter-impl.cpp b/src/todd-coxeter-impl.cpp index b4756f6cf..d9c0af2ac 100644 --- a/src/todd-coxeter-impl.cpp +++ b/src/todd-coxeter-impl.cpp @@ -267,7 +267,9 @@ semigroup. "init", [](ToddCoxeterImpl_& self, congruence_kind knd, - ToddCoxeterImpl_ const& tc) { return self.init(knd, tc); }, + ToddCoxeterImpl_ const& tc) -> ToddCoxeterImpl_& { + return self.init(knd, tc); + }, py::arg("knd"), py::arg("tc"), R"pbdoc( @@ -300,7 +302,9 @@ that it would have been in if it had just been newly constructed from "init", [](ToddCoxeterImpl_& self, congruence_kind knd, - WordGraph const& wg) { return self.init(knd, wg); }, + WordGraph const& wg) -> ToddCoxeterImpl_& { + return self.init(knd, wg); + }, py::arg("knd"), py::arg("wg"), R"pbdoc( @@ -1208,9 +1212,11 @@ standardized with respect to the order ``val`` ; and ``False`` if not. // Not exposed in the doc thing.def("internal_presentation", - &ToddCoxeterImpl_::internal_presentation); + &ToddCoxeterImpl_::internal_presentation, + py::return_value_policy::reference_internal); thing.def("spanning_tree", &ToddCoxeterImpl_::spanning_tree, + py::return_value_policy::reference_internal, R"pbdoc( :sig=(self: ToddCoxeter) -> Forest: @@ -1285,6 +1291,7 @@ The return value of this function indicates the following: [](ToddCoxeterImpl_& self) -> WordGraph const& { return self.word_graph(); }, + py::return_value_policy::reference_internal, R"pbdoc( :sig=(self: ToddCoxeter) -> WordGraph: diff --git a/src/todd-coxeter.cpp b/src/todd-coxeter.cpp index 2f8ba1dbb..80a760c5e 100644 --- a/src/todd-coxeter.cpp +++ b/src/todd-coxeter.cpp @@ -159,9 +159,8 @@ the word graph represented by *tc*. thing.def( "init", - [](ToddCoxeter_& self, congruence_kind knd, ToddCoxeter_ const& tc) { - return self.init(knd, tc); - }, + [](ToddCoxeter_& self, congruence_kind knd, ToddCoxeter_ const& tc) + -> ToddCoxeter_& { return self.init(knd, tc); }, py::arg("knd"), py::arg("tc"), R"pbdoc( @@ -216,7 +215,9 @@ semigroup. "init", [](ToddCoxeter_& self, congruence_kind knd, - WordGraph const& wg) { return self.init(knd, wg); }, + WordGraph const& wg) -> ToddCoxeter_& { + return self.init(knd, wg); + }, py::arg("knd"), py::arg("wg"), R"pbdoc( @@ -311,6 +312,7 @@ value is never :any:`UNDEFINED`. }, R"pbdoc( :sig=(i: int) -> list[int] | str: + Returns a current word representing a class with given index. This function returns the current word representing the class with index *i*. diff --git a/src/transf.cpp b/src/transf.cpp index e871e3f74..ce15a9d92 100644 --- a/src/transf.cpp +++ b/src/transf.cpp @@ -179,16 +179,15 @@ yielding these values. doc_type_name, long_name) .c_str()); - thing.def( "increase_degree_by", - [](PTransfSubclass& self, size_t m) -> void { - self.increase_degree_by(m); + [](PTransfSubclass& self, size_t m) -> PTransfSubclass& { + return static_cast(self.increase_degree_by(m)); }, py::arg("m"), fmt::format( R"pbdoc( -:sig=(self: {0}) -> None: +:sig=(self: {0}) -> {0}: Increases the degree of ``self`` in-place, leaving existing values unaltered. @@ -427,7 +426,12 @@ There are a number of helper functions for :py:class:`Transf` objects detailed b {Transf([1, 0, 2])} )pbdoc"); - // thing.attr("__name__") = "Transf"; + // This is how we could change the appearance of the + // _libsemigroups_pybind11 types in exceptions. We decided against doing + // this because it would make it more or less impossible to debug, given + // that Transf (for example) would mean the python class and any of the + // + // libsemigroups classes. thing.attr("__name__") = "Transf"; // thing.attr("__qualname__") = "Transf"; // thing.attr("__module__") = "libsemigroups_pybind11"; diff --git a/src/ukkonen.cpp b/src/ukkonen.cpp index 6255607ba..b97d809cc 100644 --- a/src/ukkonen.cpp +++ b/src/ukkonen.cpp @@ -33,12 +33,12 @@ namespace libsemigroups { namespace py = pybind11; template - void bind_ukkonen_extras(py::module& m, py::class_& uk) { + void bind_ukkonen_extras(py::module& m, py::class_& thing) { //////////////////////////////////////////////////////////////////////// // Ukkonen //////////////////////////////////////////////////////////////////////// - uk.def( + thing.def( "index", [](Ukkonen const& self, Word const& w) { return from_int(self.index(w.begin(), w.end())); @@ -65,7 +65,8 @@ suffix tree represents, then :any:`UNDEFINED` is returned. :complexity: Linear in the length of *w*. )pbdoc"); - uk.def( + + thing.def( "throw_if_contains_unique_letter", [](Ukkonen const& self, Word const& w) { return self.throw_if_contains_unique_letter(w.begin(), w.end()); @@ -232,8 +233,8 @@ represented by the :any:`Ukkonen` instance *u*. Check if a word is a suffix of any word in a suffix tree. -This function returns ``True`` if *w* is a suffix of one of the words in the suffix tree -represented by the :any:`Ukkonen` instance *u*. +This function returns ``True`` if *w* is a suffix of one of the words in the +suffix tree represented by the :any:`Ukkonen` instance *u*. :param u: the :any:`Ukkonen` object. :type u: Ukkonen @@ -541,9 +542,9 @@ the portion of *w* that was consumed in the traversal. using node_index_type = size_t; using edge_index_type = size_t; - py::class_ uk(m, - "Ukkonen", - R"pbdoc( + py::class_ thing(m, + "Ukkonen", + R"pbdoc( For an implementation of Ukkonen's algorithm. This class implements Ukkonen's algorithm for constructing a generalised suffix @@ -563,7 +564,7 @@ to the end. If a duplicate word is added, then the tree is not modified, but the //////////////////////////////////////////////////////////////////////// // State //////////////////////////////////////////////////////////////////////// - py::class_ state(uk, + py::class_ state(thing, "State", R"pbdoc( The return type of :any:`ukkonen.traverse`. @@ -613,7 +614,7 @@ Copy a :any:`Ukkonen.State` object. // Node //////////////////////////////////////////////////////////////////////// - py::class_ node(uk, + py::class_ node(thing, "Node", R"pbdoc( The type of the nodes in the tree. @@ -724,12 +725,12 @@ The length of the edge leading into the current node. // Ukkonen //////////////////////////////////////////////////////////////////////// - uk.def("__repr__", - py::overload_cast(&to_human_readable_repr)); - uk.def(py::init<>(), R"pbdoc( + thing.def("__repr__", + py::overload_cast(&to_human_readable_repr)); + thing.def(py::init<>(), R"pbdoc( Constructs an empty generalised suffix tree. )pbdoc"); - uk.def( + thing.def( "copy", [](Ukkonen const& self) { return Ukkonen(self); }, R"pbdoc( @@ -740,14 +741,14 @@ Copy a :any:`Ukkonen` object. :returns: A copy. :rtype: Ukkonen )pbdoc"); - uk.def("__copy__", [](Ukkonen const& self) { return Ukkonen(self); }); - uk.def("__iter__", [](Ukkonen const& self) { + thing.def("__copy__", [](Ukkonen const& self) { return Ukkonen(self); }); + thing.def("__iter__", [](Ukkonen const& self) { return py::make_iterator(self.begin(), self.end()); }); - uk.def("distance_from_root", - &Ukkonen::distance_from_root, - py::arg("n"), - R"pbdoc( + thing.def("distance_from_root", + &Ukkonen::distance_from_root, + py::arg("n"), + R"pbdoc( Returns the distance of a node from the root. :param n: the node. @@ -758,9 +759,9 @@ Returns the distance of a node from the root. :complexity: At worst linear in the distance of the node *n* from the root. )pbdoc"); - uk.def("init", - &Ukkonen::init, - R"pbdoc( + thing.def("init", + &Ukkonen::init, + R"pbdoc( Initialize an existing Ukkonen object. This function puts an :any:`Ukkonen` object back into the same state as if it @@ -773,10 +774,13 @@ had been newly default constructed. :any:`Ukkonen()` )pbdoc"); - uk.def("is_suffix", - &Ukkonen::is_suffix, - py::arg("st"), - R"pbdoc( + thing.def( + "is_suffix", + [](Ukkonen const& self, Ukkonen::State const& st) { + return from_int(self.is_suffix(st)); + }, + py::arg("st"), + R"pbdoc( Check if a state corresponds to a suffix. This function returns a an int if the state *st* corresponds to a suffix of any @@ -787,21 +791,21 @@ state is a suffix of if such a word exists, and :any:`UNDEFINED` otherwise. :type st: Ukkonen.State :returns: The index of a word for which *st* is a suffix, or :any:`UNDEFINED`. -:rtype: int +:rtype: int | Undefined )pbdoc"); - uk.def("length_of_distinct_words", - &Ukkonen::length_of_distinct_words, - R"pbdoc( + thing.def("length_of_distinct_words", + &Ukkonen::length_of_distinct_words, + R"pbdoc( Returns the sum of the lengths of the distinct words in the suffix tree. -:returns: The length of the distinct words . +:returns: The length of the distinct words. :rtype: int :complexity: Constant. )pbdoc"); - uk.def("length_of_words", - &Ukkonen::length_of_words, - R"pbdoc( + thing.def("length_of_words", + &Ukkonen::length_of_words, + R"pbdoc( Returns the sum of the lengths of all of the words in the suffix tree. This is the total length of all the words added to the suffix tree including duplicates, if any. @@ -811,9 +815,9 @@ This is the total length of all the words added to the suffix tree including dup :complexity: :math:`O(n)` where :math:`n` is the return value of :any:`number_of_distinct_words`. )pbdoc"); - uk.def("max_word_length", - &Ukkonen::max_word_length, - R"pbdoc( + thing.def("max_word_length", + &Ukkonen::max_word_length, + R"pbdoc( Returns the maximum length of word in the suffix tree. :returns: The maximum length of a word. @@ -821,10 +825,10 @@ Returns the maximum length of word in the suffix tree. :complexity: Constant. )pbdoc"); - uk.def("multiplicity", - &Ukkonen::multiplicity, - py::arg("i"), - R"pbdoc( + thing.def("multiplicity", + &Ukkonen::multiplicity, + py::arg("i"), + R"pbdoc( Returns the multiplicity of a word by index. This function returns the number of times that the word corresponding to the index *i* was added to the suffix tree. @@ -837,9 +841,9 @@ This function returns the number of times that the word corresponding to the ind :complexity: Constant. )pbdoc"); - uk.def("nodes", - &Ukkonen::nodes, - R"pbdoc( + thing.def("nodes", + &Ukkonen::nodes, + R"pbdoc( Returns the nodes in the suffix tree. :returns: A list of nodes. @@ -847,9 +851,9 @@ Returns the nodes in the suffix tree. :complexity: Constant. )pbdoc"); - uk.def("number_of_distinct_words", - &Ukkonen::number_of_distinct_words, - R"pbdoc( + thing.def("number_of_distinct_words", + &Ukkonen::number_of_distinct_words, + R"pbdoc( Returns the number of distinct non-empty words in the suffix tree. This is the number of distinct non-empty words added via :any:`ukkonen.add_word`. @@ -859,9 +863,9 @@ This is the number of distinct non-empty words added via :any:`ukkonen.add_word` :complexity: Constant. )pbdoc"); - uk.def("number_of_words", - &Ukkonen::number_of_words, - R"pbdoc( + thing.def("number_of_words", + &Ukkonen::number_of_words, + R"pbdoc( Returns the number of non-empty words in the suffix tree. This is the number of all words added via :any:`ukkonen.add_word` including @@ -872,11 +876,12 @@ duplicates, if any. :complexity: :math:`O(n)` where :math:`n` is the return value of :any:`number_of_distinct_words`. )pbdoc"); - uk.def("unique_letter", - &Ukkonen::unique_letter, - py::arg("i"), - R"pbdoc( -Returns the unique letter added to the end of the ``i-th`` distinct word in the suffix tree. + thing.def("unique_letter", + &Ukkonen::unique_letter, + py::arg("i"), + R"pbdoc( +Returns the unique letter added to the end of the ``i``-th distinct word in the +suffix tree. :param i: the index of an added word. :type i: int @@ -886,7 +891,7 @@ Returns the unique letter added to the end of the ``i-th`` distinct word in the :complexity: Constant. )pbdoc"); - uk.def( + thing.def( "word_index", [](Ukkonen const& self, index_type i) { return self.word_index(i); }, py::arg("i"), @@ -905,7 +910,7 @@ tree. :complexity: Constant. )pbdoc"); - uk.def( + thing.def( "word_index", [](Ukkonen const& self, Ukkonen::Node const& n) { return self.word_index(n); @@ -969,7 +974,7 @@ Returns the total number of distinct subwords of the words in the suffix tree *u :complexity: Linear in ``Ukkonen.length_of_distinct_words``. )pbdoc"); - bind_ukkonen_extras(m, uk); - bind_ukkonen_extras(m, uk); + bind_ukkonen_extras(m, thing); + bind_ukkonen_extras(m, thing); } // init_ukkonen } // namespace libsemigroups diff --git a/src/word-graph.cpp b/src/word-graph.cpp index d02330bcc..9d3c65514 100644 --- a/src/word-graph.cpp +++ b/src/word-graph.cpp @@ -17,13 +17,8 @@ // // C++ stl headers.... -#include // for array -#include // for uint32_t -#include // for uint64_t -#include // for initializer_list -#include // for string -#include // for to_string, basic_string -#include // for vector +#include // for uint32_t +#include // for vector // libsemigroups.... #include // for LIBSEMIGROUPS_EIGEN_ENABLED @@ -130,8 +125,9 @@ out-degree is specified by the length of the first item in *targets*. :type targets: list[list[int]] -:raises LibsemigroupsError: if any target is specified in *targets* is greater - than or equal to *num_nodes*. +:raises LibsemigroupsError: + if any target is specified in *targets* is greater than or equal to + *num_nodes*. .. doctest:: @@ -157,6 +153,7 @@ added. :rtype: WordGraph :complexity: Linear in ``(number_of_nodes() + nr) * out_degree()``.)pbdoc"); + thing.def("add_to_out_degree", &WordGraph_::add_to_out_degree, py::arg("nr"), @@ -175,6 +172,7 @@ increased by *nr*. :complexity: :math:`O(mn)` where ``m`` is the number of nodes, and ``n`` is the new out degree of the word graph. )pbdoc"); + thing.def( "nodes", [](WordGraph_ const& self) { @@ -225,6 +223,7 @@ to the source node *source*. This target might equal :any:`UNDEFINED`. :complexity: Constant. )pbdoc"); + thing.def("disjoint_union_inplace", &WordGraph_::disjoint_union_inplace, py::arg("that"), @@ -244,9 +243,10 @@ disjoint union of itself and *that*. The node ``n`` of *that* is mapped to :raises LibsemigroupsError: if ``self`` and *that* do not have the same out-degree. )pbdoc"); + thing.def( "induced_subgraph", - [](WordGraph_& self, node_type first, node_type last) { + [](WordGraph_& self, node_type first, node_type last) -> WordGraph_& { return self.induced_subgraph(first, last); }, py::arg("first"), @@ -276,7 +276,9 @@ subgraph induced by the range of nodes *first* to *last*. thing.def( "init", - [](WordGraph_& self, size_t m, size_t n) { return self.init(m, n); }, + [](WordGraph_& self, size_t m, size_t n) -> WordGraph_& { + return self.init(m, n); + }, py::arg("m"), py::arg("n"), R"pbdoc( @@ -533,7 +535,7 @@ with the target of the edge from the node *n* labelled *a*. Add an edge from one node to another with a given label. If *s* and *t* are nodes in ``self`` , and *a* is in the range ``[0, -out_degree())`` , then this function adds an edge from *a* to *b* labelled *a*. +out_degree())``, then this function adds an edge from *a* to *b* labelled *a*. :param s: the source node. :type s: int @@ -1468,7 +1470,7 @@ corresponding language in *y* . :raises LibsemigroupsError: if *y* has no nodes; :raises LibsemigroupsError: if *xroot* isn't a node in *x*; :raises LibsemigroupsError: if *yroot* isn't a node in *y*; -:raises LibsemigroupsError: if ``x.out_degree() != y.out_degree()``.)pbdoc"); +:raises LibsemigroupsError: if ``x.out_degree() != y.out_degree()``.)pbdoc"); meeter.def( "__call__", diff --git a/src/word-range.cpp b/src/word-range.cpp index 7bbe7935a..1c2a982e2 100644 --- a/src/word-range.cpp +++ b/src/word-range.cpp @@ -45,80 +45,6 @@ namespace libsemigroups { using size_type = typename std::vector::size_type; void init_words(py::module& m) { - //////////////////////////////////////////////////////////////////////// - // wilo.hpp - //////////////////////////////////////////////////////////////////////// - - m.def( - "wilo", - [](size_t const n, - size_t const upper_bound, - word_type const& first, - word_type const& last) { - return py::make_iterator(cbegin_wilo(n, upper_bound, first, last), - cend_wilo(n, upper_bound, first, last)); - }, - py::arg("n"), - py::arg("upper_bound"), - py::arg("first"), - py::arg("last"), - R"pbdoc( -Returns an iterator to words in lexicographic order (wilo). - -:param n: the number of letters -:type n: int -:param upper_bound: the maximum length of string to return -:type upper_bound: int -:param first: the first word -:type first: list -:param last: one past the last word -:type last: list - -:return: -An iterator to words in lexicographic order over an alphabet with *n* letters of -length at most *upper_bound* in the range ``[first, last)``. - -.. doctest:: - - >>> from libsemigroups_pybind11 import wilo - >>> list(wilo(2, 3, [0], [1, 1, 1])) - [[0], [0, 0], [0, 1], [1], [1, 0], [1, 1]] - )pbdoc"); - - //////////////////////////////////////////////////////////////////////// - // wislo.hpp - //////////////////////////////////////////////////////////////////////// - - m.def( - "wislo", - [](size_t const n, word_type const& first, word_type const& last) { - return py::make_iterator(cbegin_wislo(n, first, last), - cend_wislo(n, first, last)); - }, - py::arg("n"), - py::arg("first"), - py::arg("last"), - R"pbdoc( -Returns an iterator to words in short-lex order (wislo). - -:param n: the number of letter -:type n: int -:param first: the first word -:type first: list -:param last: one past the last word -:type last: list - -:return: -An iterator to words over an alphabet with ``n`` in short-lex -order and in the range ``[first, last)``. - -.. doctest:: - - >>> from libsemigroups_pybind11 import wislo - >>> list(wislo(2, [0], [1, 1])) - [[0], [1], [0, 0], [0, 1], [1, 0]] - )pbdoc"); - //////////////////////////////////////////////////////////////////////////// // WordRange //////////////////////////////////////////////////////////////////////////// @@ -189,8 +115,7 @@ Copy a :any:`WordRange` object. if (!to_str.can_convert_letter(w.alphabet_size() - 1)) { LIBSEMIGROUPS_EXCEPTION( "expected the alphabet size ({}) of the ToString object " - "to be " - ">= the alphabet size ({}) of the WordRange object", + "to be >= the alphabet size ({}) of the WordRange object", to_str.alphabet().size(), w.alphabet_size()); } @@ -916,10 +841,10 @@ This setting is only used if :any:`StringRange.order()` is :any:`Order.lex`. R"pbdoc( Class for converting strings to ``list[int]`` with specified alphabet. -An instance of this class is callable and used to convert from :any:`str` to ``list[int]``. -The characters in the string are converted to integers according to their -position in the alphabet used to construct a :any:`ToWord` instance if one is -provided, or using :any:`words.human_readable_index` otherwise. +An instance of this class is callable and used to convert from :any:`str` to +``list[int]``. The characters in the string are converted to integers according +to their position in the alphabet used to construct a :any:`ToWord` instance if +one is provided, or using :any:`words.human_readable_index` otherwise. .. doctest:: @@ -998,8 +923,8 @@ performed using :any:`words.human_readable_index`. R"pbdoc( Check if the current ToWord instance can convert a specified letter. -This function returns ``True`` if *c* can can be converted to an :any:`int` using this -ToWord instance, and ``False`` otherwise. +This function returns ``True`` if *c* can be converted to an :any:`int` using +this :any:`ToWord` instance, and ``False`` otherwise. :param c: the letter to check the convertibility of. :type c: str @@ -1066,21 +991,24 @@ via :any:`ToWord.init()` , or with :any:`words.human_readable_index` if :param input: the string to convert. :type input: str -:raises LibsemigroupsError: if the alphabet used to define an instance of :any:`ToWord` is not empty and *input* contains letters that do not correspond to letters of the alphabet. +:raises LibsemigroupsError: + if the alphabet used to define an instance of :any:`ToWord` is not empty and + *input* contains letters that do not correspond to letters of the alphabet. )pbdoc"); //////////////////////////////////////////////////////////////////////////// // ToString //////////////////////////////////////////////////////////////////////////// + py::class_ thing4(m, "ToString", R"pbdoc( Class for converting ``list[int]`` to strings with specified alphabet. -An instance of this class is callable and used to convert from ``list[int]`` to :any:`str`. -The integers in the list are converted to characters according to their -position in the alphabet used to construct a :any:`ToString` instance if one is -provided, or using :any:`words.human_readable_letter` otherwise. +An instance of this class is callable and used to convert from ``list[int]`` to +:any:`str`. The integers in the list are converted to characters according to +their position in the alphabet used to construct a :any:`ToString` instance if +one is provided, or using :any:`words.human_readable_letter` otherwise. .. doctest:: @@ -1149,7 +1077,7 @@ performed using :any:`words.human_readable_letter`. R"pbdoc( Check if the current ToString instance can convert a specified letter. -This function returns ``True`` if *l* can can be converted to a :any:`str` using this +This function returns ``True`` if *l* can be converted to a :any:`str` using this ToString instance, and ``False`` otherwise. :param l: the letter to check the convertibility of. diff --git a/tests/cong_common.py b/tests/cong_common.py new file mode 100644 index 000000000..cfa005614 --- /dev/null +++ b/tests/cong_common.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# pylint: disable=no-name-in-module, missing-function-docstring +# pylint: disable=missing-class-docstring, invalid-name + +# Copyright (c) 2021-2024 J. D. Mitchell +# +# Distributed under the terms of the GPL license version 3. +# +# The full license is in the file LICENSE, distributed with this software. + +""" +This module contains some functions used in tests for derived classes of +CongruenceCommon. +""" + +# pylint: disable=no-name-in-module, missing-function-docstring, invalid-name + +from libsemigroups_pybind11 import ( + Presentation, + congruence_kind, + presentation, +) + + +def check_congruence_common_return_policy(TestType): + p = Presentation("ab") + presentation.add_rule(p, "abab", "a" * 6) + presentation.add_rule(p, "ba", "ababbb") + + c = TestType(congruence_kind.twosided, p) + assert c.init() is c + assert c.init(congruence_kind.twosided, p) is c + assert c.add_generating_pair("a", "b") is c + + assert c.presentation() is c.presentation() + assert c.generating_pairs() is not c.generating_pairs() + return c diff --git a/tests/test_action.py b/tests/test_action.py index 40c119166..4801f8877 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -471,3 +471,14 @@ def test_action_word_graph(right_actions, left_actions): assert ( wg.number_of_edges() == len(action) * action.number_of_generators() ) + + +def test_action_return_policy(right_actions): + action = right_actions[0] + assert action.reserve(10) is action + x = BMat8([[1, 1, 1, 0], [1, 1, 0, 0], [0, 1, 0, 1], [0, 0, 0, 0]]) + seed = row_space_basis(x) + assert action.add_seed(seed) is action + assert action.add_generator(x) is action + assert action.cache_scc_multipliers(False) is action + assert action.init() is action diff --git a/tests/test_aho_corasick.py b/tests/test_aho_corasick.py index a1d7a62c6..4a22d6b3b 100644 --- a/tests/test_aho_corasick.py +++ b/tests/test_aho_corasick.py @@ -163,3 +163,8 @@ def test_is_terminal(): with pytest.raises(LibsemigroupsError): ac.is_terminal(1000) + + +def test_aho_corasick_return_policy(): + ac = basic_ac() + assert ac.init() is ac diff --git a/tests/test_bipart.py b/tests/test_bipart.py index 5d7ed02aa..0a01b0662 100644 --- a/tests/test_bipart.py +++ b/tests/test_bipart.py @@ -109,3 +109,11 @@ def test_helpers(): assert bipartition.underlying_partition(x) == [[1, 2], [-1, -2]] assert blocks.underlying_partition(x.left_blocks()) == [[1, 2]] assert blocks.underlying_partition(x.right_blocks()) == [[1, 2]] + + +def test_bipartition_return_policy(): + x = Bipartition([[1, 2], [-1, -2]]) + assert x.right_blocks() is not x.right_blocks() + assert x.right_blocks() == x.right_blocks() + assert x.left_blocks() is not x.left_blocks() + assert x.left_blocks() == x.left_blocks() diff --git a/tests/test_cong.py b/tests/test_cong.py index 88f775240..9bfd6308d 100644 --- a/tests/test_cong.py +++ b/tests/test_cong.py @@ -33,6 +33,8 @@ presentation, ) +from .cong_common import check_congruence_common_return_policy + def test_018(): ReportGuard(False) @@ -590,3 +592,9 @@ def test_partition(): ["fdg", "fef"], ["gdg", "gef"], ] + + +def test_congruence_return_policy(): + c = check_congruence_common_return_policy(Congruence) + + assert c.max_threads(2) is c diff --git a/tests/test_dot.py b/tests/test_dot.py index 1b20b5d61..a9015440a 100644 --- a/tests/test_dot.py +++ b/tests/test_dot.py @@ -148,3 +148,27 @@ def test_dot_kind(): assert d.kind() == Dot.Kind.digraph d.kind(Dot.Kind.graph) assert d.kind() == Dot.Kind.graph + + +def test_dot_return_policy(): + d = Dot() + assert d.colors is not d.colors + with pytest.raises(AttributeError): + d.colors = [""] + assert d.add_node("Sir Lancelot") is d.node("Sir Lancelot") + assert d.add_edge("Sir Lancelot", "Sir Lancelot") is d.edge( + "Sir Lancelot", "Sir Lancelot" + ) + assert d.add_subgraph(d) is d + assert d.edges() is not d.edges() + assert d.subgraphs() is not d.subgraphs() + assert d.attrs() is not d.attrs() + assert d.nodes() is not d.nodes() + with pytest.raises(LibsemigroupsError): + assert d.node("Sir Lcanelot") + assert d.node("Sir Lancelot") is d.node("Sir Lancelot") + assert d.edge("Sir Lancelot", "Sir Lancelot") is d.edge( + "Sir Lancelot", "Sir Lancelot" + ) + assert d.add_attr("shape", "box") is d + assert d.add_attr("cannot think") is d diff --git a/tests/test_forest.py b/tests/test_forest.py new file mode 100644 index 000000000..dc9405dd9 --- /dev/null +++ b/tests/test_forest.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2025, James D. Mitchell +# +# Distributed under the terms of the GPL license version 3. +# +# The full license is in the file LICENSE, distributed with this software. + +# pylint: disable=missing-function-docstring, redefined-outer-name + +""" +This module contains some tests for the libsemigroups_pybind11 functionality +arising from forest.*pp in libsemigroups. +""" + +import pytest + +from libsemigroups_pybind11 import Forest, UNDEFINED + + +@pytest.fixture +def forest_fixture(): + return Forest([UNDEFINED, 0, 1, 2, 3], [UNDEFINED, 0, 0, 0, 0]) + + +def test_forest_return_policy(forest_fixture): + f = forest_fixture + assert f.add_nodes(2) is f + assert f.init(2) is f + assert f.labels() is not f.labels() + assert f.parents() is not f.parents() + assert f.labels() is not f.labels() + assert f.set_parent_and_label(1, 0, 10) is f diff --git a/tests/test_froidure_pin.py b/tests/test_froidure_pin.py index df33a74cd..85085e0df 100644 --- a/tests/test_froidure_pin.py +++ b/tests/test_froidure_pin.py @@ -14,7 +14,6 @@ from datetime import timedelta import pytest -from .runner import check_runner from libsemigroups_pybind11 import ( FroidurePin, @@ -32,6 +31,8 @@ UNDEFINED, ) +from .runner import check_runner + def check_constructors(coll): ReportGuard(False) @@ -588,6 +589,40 @@ def test_froidure_pin_return_undefined_1(): assert S.current_position(Perm([1, 0, 2])) == UNDEFINED +def test_froidure_pin_return_policy(): + S = FroidurePin(Perm([1, 0, 2, 3, 4, 5, 6]), Perm([1, 2, 3, 4, 5, 6, 0])) + assert S.batch_size(10) is S + assert S.current_left_cayley_graph() is S.current_left_cayley_graph() + assert S.current_right_cayley_graph() is S.current_right_cayley_graph() + assert S.enumerate(10) is None + assert S.left_cayley_graph() is S.left_cayley_graph() + assert S.right_cayley_graph() is S.right_cayley_graph() + + it1, it2 = S.current_elements(), S.current_elements() + for x, y in zip(it1, it2): + assert x == y + + assert S.add_generator(S.generator(0)) is S + assert S.add_generators([S.generator(0)]) is S + assert S.closure([S.generator(0)]) is S + assert S.copy_add_generators([S.generator(0)]) is not S + assert S.copy_closure([S.generator(0)]) is not S + assert S.generator(0) is S.generator(0) + assert S.init() is S + assert ( + S.init([Perm([1, 0, 2, 3, 4, 5, 6]), Perm([1, 2, 3, 4, 5, 6, 0])]) is S + ) + assert S.reserve(10) is S + assert S.sorted_at(0) is S.sorted_at(0) + + # TODO the next comparison doesn't currently work because + # does not cache its returned values, but the value returned by the C++ + # function froidure_pin_to_element is a reference. + assert froidure_pin.to_element(S, [0, 1, 0]) is not froidure_pin.to_element( + S, [0, 1, 0] + ) + + # def test_froidure_pin_tce(checks_for_froidure_pin): # ReportGuard(False) # tc = ToddCoxeter(congruence_kind.twosided) diff --git a/tests/test_gabow.py b/tests/test_gabow.py index 150c13a69..b313e735a 100644 --- a/tests/test_gabow.py +++ b/tests/test_gabow.py @@ -11,9 +11,9 @@ libsemigroups_pybind11. """ -# pylint: disable=no-name-in-module, missing-function-docstring, invalid-name, -# pylint: disable=duplicate-code, too-many-lines +# pylint: disable=missing-function-docstring, redefined-outer-name +import pytest from libsemigroups_pybind11 import ( Gabow, @@ -22,6 +22,15 @@ ) +@pytest.fixture +def word_graph_fixture(): + w = WordGraph(17, 31) + for i in range(17): + for j in range(31): + w.target(i, j, (7 * i + 23 * j) % 17) + return w + + def test_001(): w = WordGraph(17, 31) for i in range(17): @@ -104,3 +113,18 @@ def test_004(): assert w is g.word_graph() assert list(g.roots()) == [32, 65, 98, 131, 164, 197, 230, 263, 296, 329] + + +def test_gabow_return_policy(word_graph_fixture): + wg = word_graph_fixture + g = Gabow(wg) + assert g.component(0) is not g.component(0) + assert g.component_of(0) is not g.component_of(0) + assert g.components() is not g.components() + + assert g.init(wg) is g + assert not g.has_components() + assert not g.has_components() + assert g.reverse_spanning_forest() is g.reverse_spanning_forest() + assert g.spanning_forest() is g.spanning_forest() + assert g.word_graph() is g.word_graph() diff --git a/tests/test_kambites.py b/tests/test_kambites.py index 220e1d340..52555ea4f 100644 --- a/tests/test_kambites.py +++ b/tests/test_kambites.py @@ -6,11 +6,10 @@ # The full license is in the file LICENSE, distributed with this software. """ -This module contains some tests for the Presentation class. +This module contains some tests for the Kambites class. """ -# pylint: disable=fixme, missing-function-docstring -# pylint: disable=missing-class-docstring, invalid-name +# pylint: disable=missing-function-docstring from libsemigroups_pybind11 import ( POSITIVE_INFINITY, @@ -26,6 +25,8 @@ StringRange, ) +from .cong_common import check_congruence_common_return_policy + ############################################################################### # Helper functions ############################################################################### @@ -203,10 +204,8 @@ def test_case_006_c(): p = Presentation("abcdefghi") presentation.add_rule( p, - "adichhbhibfchbfbbibaidfibifgagcgdedfeeibhggdbchfdaefbefcbaa" - "hcbhbidgaahbahhahhb", - "edfeeibhggdbchfdaefbeadichhbhibfchbfbbibaiihebabeabahcgdbic" - "bgiciffhfggbfadf", + "adichhbhibfchbfbbibaidfibifgagcgdedfeeibhggdbchfdaefbefcbaahcbhbidgaahbahhahhb", + "edfeeibhggdbchfdaefbeadichhbhibfchbfbbibaiihebabeabahcgdbicbgiciffhfggbfadf", ) presentation.add_rule( p, @@ -331,3 +330,9 @@ def test_case_010(): assert len(strings) == 81 strings = StringRange().alphabet("cab").first("cccc").last("ccccc") assert sum(1 for x in strings if k.contains(x, "acba")) == 2 + + +def test_kambites_return_policy(): + k = check_congruence_common_return_policy(Kambites) + + assert k.ukkonen() is k.ukkonen() diff --git a/tests/test_knuth_bendix.py b/tests/test_knuth_bendix.py index 861ee5d0b..bf0a15e98 100644 --- a/tests/test_knuth_bendix.py +++ b/tests/test_knuth_bendix.py @@ -10,24 +10,28 @@ This module contains some tests for KnuthBendix. """ -# pylint: disable=no-name-in-module, missing-function-docstring, invalid-name +# pylint: disable=missing-function-docstring from datetime import timedelta import pytest -from .runner import check_runner + from libsemigroups_pybind11 import ( KnuthBendix, - congruence_kind, - ReportGuard, - Presentation, - presentation, + LIMIT_MAX, LibsemigroupsError, POSITIVE_INFINITY, - is_obviously_infinite, + Presentation, + ReportGuard, StringRange, + congruence_kind, + is_obviously_infinite, knuth_bendix, + presentation, ) +from .runner import check_runner +from .cong_common import check_congruence_common_return_policy + def check_initialisation(*args): for rewriter in ["RewriteFromLeft", "RewriteTrie"]: @@ -371,6 +375,24 @@ def test_non_trivial_classes(): ] +def test_knuth_bendix_return_policy(): + kb = check_congruence_common_return_policy(KnuthBendix) + assert kb.max_pending_rules(10) is kb.max_pending_rules(10) + assert kb.check_confluence_interval( + LIMIT_MAX + ) is kb.check_confluence_interval(LIMIT_MAX) + assert kb.max_overlap(POSITIVE_INFINITY) is kb.max_overlap( + POSITIVE_INFINITY + ) + assert kb.max_rules(POSITIVE_INFINITY) is kb.max_rules(POSITIVE_INFINITY) + assert kb.overlap_policy() is not kb.overlap_policy() + assert kb.overlap_policy(kb.options.overlap.ABC) is kb.overlap_policy( + kb.options.overlap.ABC + ) + assert kb.gilman_graph() is kb.gilman_graph() + assert kb.gilman_graph_node_labels() is not kb.gilman_graph_node_labels() + + # TODO(0) Does the alphabet bug persist? YES: the test fails # def test_alphabet_bug(): # p = Presentation("".join(chr(i) for i in range(-126, 128))) diff --git a/tests/test_konieczny.py b/tests/test_konieczny.py index 586cc52f4..0c5c68b33 100644 --- a/tests/test_konieczny.py +++ b/tests/test_konieczny.py @@ -9,8 +9,7 @@ This module contains some tests for the Konieczny class. """ -# pylint: disable=fixme, missing-function-docstring, no-name-in-module -# pylint: disable=missing-class-docstring, invalid-name, duplicate-code +# pylint: disable=missing-function-docstring, invalid-name from datetime import timedelta @@ -457,3 +456,24 @@ def test_case_030(): assert S.size() == 21033 with pytest.raises(RuntimeError): S.add_generator(gens[0]) + + +def test_konieczny_return_policy(): + gens = [ + PPerm([0, 2, 3, 7], [1, 6, 7, 3], 9), + PPerm([0, 1, 2, 3, 4, 7], [6, 5, 8, 0, 2, 1], 9), + PPerm([0, 1, 2, 3, 4, 5, 6, 8], [1, 7, 2, 6, 0, 4, 8, 5], 9), + PPerm([0, 1, 2, 3, 5, 6, 8], [2, 4, 6, 1, 5, 8, 7], 9), + PPerm([0, 1, 2, 3, 5, 8], [7, 3, 6, 4, 2, 5], 9), + ] + + S = Konieczny(gens) + assert S.copy() is not S + assert S.add_generator(gens[0]) is S + assert S.add_generators(gens) is S + assert S.D_class_of_element(gens[0]) is S.D_class_of_element(gens[0]) + assert S.generator(0) is S.generator(0) + assert ( + S.D_class_of_element(gens[0]).rep() + is S.D_class_of_element(gens[0]).rep() + ) diff --git a/tests/test_matrix.py b/tests/test_matrix.py index b5eac3b31..8d4742630 100644 --- a/tests/test_matrix.py +++ b/tests/test_matrix.py @@ -162,3 +162,25 @@ def test_hash(matrix_kinds): x = make_mat(T, [[0, 1, 1], [1, 0, 1], [1, 1, 1]]) d = {x: True} assert x in d + + +def test_matrix_return_policy(matrix_kinds): + for T in matrix_kinds: + x = make_mat(T, [[0, 1, 1], [1, 0, 1], [1, 1, 1]]) + y = x.copy() + assert x is not y + + x *= 2 + assert x == 2 * y + + x += x + y = 2 * y + assert x == y + y + + x += 2 + y = y + y + assert x == y + 2 + + assert x.transpose() is not x + assert x.row(0) is not x.row(0) + assert x.rows() is not x.rows() diff --git a/tests/test_paths.py b/tests/test_paths.py index 9c5dec155..7c99d0cac 100644 --- a/tests/test_paths.py +++ b/tests/test_paths.py @@ -117,3 +117,18 @@ def test_paths_bug2(): p.source(0) assert len(p) == sys.maxsize assert p.count() == POSITIVE_INFINITY + + +def test_paths_return_policy(): + wg = WordGraph(4, [[0, 1], [1, 0], [2, 2]]) + p = Paths(wg) + + assert p.copy() is not p + + assert p.init(wg) is p + assert p.max(10) is p + assert p.min(9) is p + assert p.order(Order.lex) is p + assert p.source(0) is p + assert p.target(2) is p + assert p.word_graph() is p.word_graph() diff --git a/tests/test_pbr.py b/tests/test_pbr.py index 4405e9836..704f9de86 100644 --- a/tests/test_pbr.py +++ b/tests/test_pbr.py @@ -119,3 +119,9 @@ def test_rank_degree(): assert x.degree() == 3 assert pbr.one(x).degree() == 3 assert PBR([[0, 1, 2]] * 16).degree() == 8 + + +def test_pbr_return_policy(): + x = PBR([[0, 1, 2]] * 6) + + assert x.copy() is not x diff --git a/tests/test_present.py b/tests/test_present.py index 0a6761e91..2e0209064 100644 --- a/tests/test_present.py +++ b/tests/test_present.py @@ -1076,3 +1076,29 @@ def test_inverses_039(): def test_remove_generator_040(): check_remove_generator(to_string) check_remove_generator(to_word) + + +def test_presentation_return_policy(): + p = Presentation([0, 1, 2]) + presentation.add_rule(p, [0, 0, 0], [0]) + + assert p.copy() is not p + assert p.alphabet() is not p.alphabet() + assert p.alphabet(5) is p + assert p.alphabet([1]) is p + assert p.alphabet_from_rules() is p + assert p.contains_empty_word(False) is p + assert p.init() is p + assert p.add_generator(2) is p + assert p.remove_generator(2) is p + + p = Presentation("abc") + assert p.copy() is not p + assert p.alphabet() is not p.alphabet() + assert p.alphabet(5) is p + assert p.alphabet_from_rules() is p + assert p.alphabet("a") is p + assert p.contains_empty_word(False) is p + assert p.init() is p + assert p.add_generator("b") is p + assert p.remove_generator("b") is p diff --git a/tests/test_runner.py b/tests/test_runner.py index bf7539d6f..373af7606 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -36,10 +36,11 @@ def test_reporter_000(): r.init() assert r.report_prefix() == "" assert r.report_every() == timedelta(seconds=1) - r.report_prefix("Banana") + assert r.report_prefix("Banana") is r r.report_every(timedelta(seconds=32)) s = r.copy() + assert s is not r assert s.report_prefix() == "Banana" assert s.report_every() == timedelta(seconds=32) assert s.last_report() == r.last_report() diff --git a/tests/test_schreier_sims.py b/tests/test_schreier_sims.py index 39b4ca02d..647eda56c 100644 --- a/tests/test_schreier_sims.py +++ b/tests/test_schreier_sims.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- -# pylint: disable=no-name-in-module, missing-function-docstring -# pylint: disable=missing-class-docstring, missing-module-docstring # pylint: disable=invalid-name, redefined-outer-name +# pylint: disable=missing-function-docstring # Copyright (c) 2024 Joseph Edwards # @@ -9,6 +8,8 @@ # # The full license is in the file LICENSE, distributed with this software. +"""This file contains test for SchreierSims""" + # TODO(0): # * test number_of_strong_generators # * test strong_generator @@ -560,3 +561,25 @@ def test_SchreierSims_Perm1(checks_with_int): def test_SchreierSims_Perm2(checks_with_int): for check in checks_with_int: check(511) + + +def test_SchreierSims_return_policy(): + gens = [ + Perm([0, 2, 4, 6, 7, 3, 8, 1, 5] + list(range(9, 511))), + Perm([0, 3, 5, 4, 8, 7, 2, 6, 1] + list(range(9, 511))), + ] + S = SchreierSims(gens) + assert S.copy() is not S + assert S.add_base_point(0) is S + assert S.generator(0) is S.generator(0) + assert S.inverse_transversal_element(0, 1) is S.inverse_transversal_element( + 0, 1 + ) + assert S.transversal_element(0, 1) is S.transversal_element(0, 1) + assert S.strong_generator(0, 1) is S.strong_generator(0, 1) + assert S.one() is S.one() + assert S.sift(gens[0]) is not gens[0] + S.sift_inplace(gens[0]) + assert gens[0] == S.one() + + assert S.init() is S diff --git a/tests/test_sims.py b/tests/test_sims.py index 59a2d24af..8f0bfad34 100644 --- a/tests/test_sims.py +++ b/tests/test_sims.py @@ -26,6 +26,7 @@ Order, Presentation, ReportGuard, + RepOrc, Sims1, Sims2, SimsRefinerFaithful, @@ -521,12 +522,86 @@ def test_sims_refiner_ideals_902(): assert s.number_of_threads(8).number_of_congruences(7) == 12 -def test_sims_return_policy(): +def test_sims1_return_policy(): p = Presentation("ab") p.rules = ["a" * 5, "a", "b" * 4, "b", "ab", "ba"] - s = Sims1(to(p, Return=(Presentation, list[int]))) + q = to(p, Return=(Presentation, list[int])) + s = Sims1(q) assert s.presentation() is s.presentation() + assert s.presentation(q) is s + assert s.number_of_threads(2) is s + assert s.first_long_rule_position(0) is s + assert s.clear_long_rules() is s + assert s.long_rule_length(0) is s + assert s.pruners() is not s.pruners() + assert s.add_pruner(lambda wg: True) is s + assert s.pruners() is not s.pruners() + assert s.clear_pruners() is s + assert s.included_pairs() is not s.included_pairs() + assert s.add_included_pair([0, 1], [0]) is s + assert s.clear_included_pairs() is s + assert s.excluded_pairs() is not s.excluded_pairs() + assert s.add_excluded_pair([0, 1], [0]) is s + assert s.clear_excluded_pairs() is s + assert s.idle_thread_restarts(2) is s + assert s.init(q) is s + assert s.copy() is not s + + +def test_sims2_return_policy(): + p = Presentation("ab") + p.rules = ["a" * 5, "a", "b" * 4, "b", "ab", "ba"] + q = to(p, Return=(Presentation, list[int])) + s = Sims2(q) + assert s.presentation() is s.presentation() + assert s.presentation(q) is s + assert s.number_of_threads(2) is s + assert s.first_long_rule_position(0) is s + assert s.clear_long_rules() is s + assert s.long_rule_length(0) is s + assert s.pruners() is not s.pruners() + assert s.add_pruner(lambda wg: True) is s + assert s.pruners() is not s.pruners() + assert s.clear_pruners() is s + assert s.included_pairs() is not s.included_pairs() + assert s.add_included_pair([0, 1], [0]) is s + assert s.clear_included_pairs() is s + assert s.excluded_pairs() is not s.excluded_pairs() + assert s.add_excluded_pair([0, 1], [0]) is s + assert s.clear_excluded_pairs() is s + assert s.idle_thread_restarts(2) is s + assert s.init(q) is s + assert s.copy() is not s + + +def test_rep_orc_return_policy(): + ro = RepOrc() + assert ro.init() is ro + assert ro.max_nodes(10) is ro + assert ro.min_nodes(9) is ro + assert ro.target_size(27) is ro + + +def test_min_rep_orc_return_policy(): + ro = MinimalRepOrc() + assert ro.init() is ro + assert ro.target_size(27) is ro + + +def test_sims_refiner_faithful_return_policy(): + srf = SimsRefinerFaithful() + + assert srf.init() is srf + assert srf.init([[0, 1], [0]]) is srf + + +def test_sims_refiner_ideals_return_policy(): sri = SimsRefinerIdeals() assert sri.presentation() is sri.presentation() + assert sri.init() is sri + p = Presentation("ab") + p.rules = ["a" * 5, "a", "b" * 4, "b", "ab", "ba"] + q = to(p, Return=(Presentation, list[int])) + assert sri.init(q) is sri diff --git a/tests/test_stephen.py b/tests/test_stephen.py index 2e9f39e44..561d66e66 100644 --- a/tests/test_stephen.py +++ b/tests/test_stephen.py @@ -1656,3 +1656,19 @@ def test_stephen_051(): Si *= Ti Si.run() assert Si == S + + +@pytest.mark.quick +def test_stephen_return_policy(): + ReportGuard(False) + to_word = ToWord("abcABC") + + p = InversePresentation(to_word("abcABC")) + p.inverses(to_word("ABCabc")) + + S = Stephen(p) + + assert S.copy() is not S + assert S.init(p) is S + assert S.set_word([0, 1]) is S + assert S.word_graph() is S.word_graph() diff --git a/tests/test_todd_coxeter.py b/tests/test_todd_coxeter.py index d4727a7be..7f83a0e05 100644 --- a/tests/test_todd_coxeter.py +++ b/tests/test_todd_coxeter.py @@ -31,6 +31,9 @@ word_graph, ) +from .cong_common import check_congruence_common_return_policy + + strategy = ToddCoxeter.options.strategy @@ -374,3 +377,45 @@ def test_current_word_of(): assert tree.number_of_nodes() == 0 assert wg is tc.word_graph() assert wg.number_of_nodes() == 1 + + +def test_todd_coxeter_return_policy(): + options = ToddCoxeter.options + tc = check_congruence_common_return_policy(ToddCoxeter) + # Initializers + assert tc.init(congruence_kind.twosided, tc) is tc + assert tc.init(congruence_kind.twosided, tc.current_word_graph()) is tc + + # Options + assert tc.def_max(10) is tc + assert tc.def_policy(options.def_policy.discard_all_if_no_space) is tc + assert tc.def_version(options.def_version.one) is tc + assert tc.f_defs(10) is tc + assert tc.hlt_defs(10) is tc + assert tc.large_collapse(10) is tc + assert tc.lookahead_extent(options.lookahead_extent.full) is tc + assert tc.lookahead_growth_factor(1.1) is tc + assert tc.lookahead_growth_threshold(5 * 10**6) is tc + assert tc.lookahead_min(5 * 10**6) is tc + assert tc.lookahead_next(5 * 10**6) is tc + assert tc.lookahead_stop_early_interval(timedelta(seconds=0.1)) is tc + assert tc.lookahead_stop_early_ratio(0.1) is tc + assert tc.lookahead_style(options.lookahead_style.felsch) is tc + assert tc.lower_bound(10) is tc + assert tc.save(False) is tc + assert tc.strategy(options.strategy.felsch) is tc + assert tc.use_relations_in_extra(False) is tc + + # Other methods + + assert tc.current_spanning_tree() is tc.current_spanning_tree() + assert tc.current_word_graph() is tc.current_word_graph() + assert tc.internal_presentation() is tc.internal_presentation() + assert tc.presentation() is tc.presentation() + + p = Presentation("a") + presentation.add_rule(p, "aa", "a") + tc.init(congruence_kind.twosided, p) + + assert tc.spanning_tree() is tc.spanning_tree() + assert tc.word_graph() is tc.word_graph() diff --git a/tests/test_transf.py b/tests/test_transf.py index 7b562a50e..da5531ef2 100644 --- a/tests/test_transf.py +++ b/tests/test_transf.py @@ -376,3 +376,11 @@ def test_corner_cases(): assert PPerm([], [], 0) * PPerm([], [], 0) == PPerm([], [], 0) assert PPerm([], [], 10) * PPerm([], [], 10) == PPerm([], [], 10) assert PPerm([], [], 256) * PPerm([], [], 256) == PPerm([], [], 256) + + +def test_transf_return_policy(): + for TestType in (Transf, PPerm, Perm): + x = TestType([0]) + assert x.copy() is not x + assert x.images() is not x.images() + assert x.increase_degree_by(2) is x diff --git a/tests/test_ukkonen.py b/tests/test_ukkonen.py index 8e19294d9..c7c1f9f82 100644 --- a/tests/test_ukkonen.py +++ b/tests/test_ukkonen.py @@ -103,7 +103,12 @@ def test_000_c(): assert ukkonen.number_of_distinct_subwords(t) == 25 assert not ukkonen.is_suffix(t, [1, 2, 3, 5]) - assert ukkonen.is_suffix(t, [1, 2, 3]) + + st, w = ukkonen.traverse(t, [1, 2, 3, 5]) + assert not (len(w) == 4 and t.is_suffix(st)) + st, w = ukkonen.traverse(t, [5, 5]) + with pytest.raises(LibsemigroupsError): + t.is_suffix(st) assert ukkonen.is_suffix(t, []) assert ukkonen.is_suffix(t, [0, 0, 4, 0, 0, 0]) @@ -382,3 +387,28 @@ def test_008(): assert t.number_of_distinct_words() == 0 assert t.number_of_words() == 0 assert len(t.nodes()) == 1 + + +def test_ukkonen_return_policy(): + kknn = Ukkonen() + ukkonen.add_words( + kknn, + [ + [0, 1, 2], + [0, 1, 2], + [0, 1, 2], + [0], + [1], + [2], + [4, 2, 1, 2, 3, 4], + [4, 2, 1, 2], + [2, 3, 4], + [1, 2, 3, 4], + [4, 2, 3, 4], + ], + ) + assert kknn.copy() is not kknn + assert kknn.init() is kknn + + st, _ = ukkonen.traverse(kknn, [1, 2, 3, 5]) + assert kknn.is_suffix(st) == UNDEFINED diff --git a/tests/test_word_graph.py b/tests/test_word_graph.py index 8377efd83..edb3fbde7 100644 --- a/tests/test_word_graph.py +++ b/tests/test_word_graph.py @@ -349,3 +349,31 @@ def test_random(): assert str(word_graph.dot(d)).startswith( 'digraph WordGraph {\n\n 0 [shape="box"]\n 1 [shape="box"]\n' ) + + +def test_word_graph_return_policy(): + wg = WordGraph.random(5, 5) + assert wg.copy() is not wg + assert wg.add_nodes(2) is wg + assert wg.add_to_out_degree(2) is wg + with pytest.raises(LibsemigroupsError): + wg.disjoint_union_inplace(wg) + + assert wg.disjoint_union_inplace(wg.copy()) is wg + assert wg.induced_subgraph(0, 7) is wg + assert wg.init(5, 5) is wg + assert wg.remove_all_targets() is wg + assert wg.remove_label(0) is wg + assert wg.remove_target(0, 1) is wg + assert wg.reserve(5, 5) is wg + assert wg.swap_targets(3, 2, 0) is wg + + +def test_meeter_return_policy(): + meet = Meeter() + assert meet.copy() is not meet + + +def test_joiner_return_policy(): + join = Joiner() + assert join.copy() is not join diff --git a/tests/test_words.py b/tests/test_word_range.py similarity index 91% rename from tests/test_words.py rename to tests/test_word_range.py index 440cbe0ab..49f4c7c15 100644 --- a/tests/test_words.py +++ b/tests/test_word_range.py @@ -9,7 +9,7 @@ # pylint: disable=missing-function-docstring, invalid-name """ -This module contains some tests for the functionality in words.*pp. +This module contains some tests for the functionality in words-range.*pp. """ import pytest @@ -350,3 +350,36 @@ def test_human_readable_index(): assert human_readable_index("\377") == 255 with pytest.raises(ValueError): human_readable_index("Ā") # this is character 256 + + +def test_ranges_return_policy(): + for TestType in (WordRange, StringRange): + r = TestType() + assert r.copy() is not r + assert r.init() is r + assert r.max(2) is r + assert r.min(2) is r + assert r.order(Order.shortlex) is r + assert r.upper_bound(10) is r + + r = WordRange() + assert r.alphabet_size(2) is r + assert r.first([0] * 2) is r + assert r.last([1] * 2) is r + + r = StringRange() + assert r.alphabet("ab") is r + assert r.first("a" * 2) is r + assert r.last("b" * 2) is r + + +def test_to_return_policy(): + for TestType in (ToWord, ToString): + t = TestType() + assert t.copy() is not t + assert t.init() is t + assert t.init("bc") is t + + t = ToWord() + + t = ToString()