diff --git a/docs/source/main-algorithms/cong-intf/index.rst b/docs/source/main-algorithms/cong-intf/index.rst index e46c231e..c0a86a30 100644 --- a/docs/source/main-algorithms/cong-intf/index.rst +++ b/docs/source/main-algorithms/cong-intf/index.rst @@ -15,7 +15,7 @@ semigroups and monoids. These classes are: * :any:`Congruence` * :any:`Kambites` -* :any:`KnuthBendix` +* :any:`KnuthBendixRewriteTrie` * :any:`ToddCoxeter` .. toctree:: diff --git a/docs/source/main-algorithms/knuth-bendix/class.rst b/docs/source/main-algorithms/knuth-bendix/class.rst new file mode 100644 index 00000000..326db972 --- /dev/null +++ b/docs/source/main-algorithms/knuth-bendix/class.rst @@ -0,0 +1,50 @@ +.. 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. + +.. currentmodule:: _libsemigroups_pybind11 + +The KnuthBendix class +===================== + +.. autoclass:: KnuthBendixRewriteTrie + :doc-only: + :class-doc-from: class + +Contents +-------- + +.. autosummary:: + :nosignatures: + + ~KnuthBendixRewriteTrie + KnuthBendixRewriteTrie.active_rules + KnuthBendixRewriteTrie.check_confluence_interval + KnuthBendixRewriteTrie.confluent + KnuthBendixRewriteTrie.confluent_known + KnuthBendixRewriteTrie.copy + KnuthBendixRewriteTrie.contains + KnuthBendixRewriteTrie.currently_contains + KnuthBendixRewriteTrie.generating_pairs + KnuthBendixRewriteTrie.gilman_graph + KnuthBendixRewriteTrie.gilman_graph_node_labels + KnuthBendixRewriteTrie.max_overlap + KnuthBendixRewriteTrie.max_pending_rules + KnuthBendixRewriteTrie.max_rules + KnuthBendixRewriteTrie.number_of_active_rules + KnuthBendixRewriteTrie.number_of_classes + KnuthBendixRewriteTrie.number_of_inactive_rules + KnuthBendixRewriteTrie.overlap_policy + KnuthBendixRewriteTrie.presentation + KnuthBendixRewriteTrie.reduce_no_run + KnuthBendixRewriteTrie.total_rules + +Full API +-------- + +.. autoclass:: KnuthBendixRewriteTrie + :class-doc-from: init + :members: + :exclude-members: options diff --git a/docs/source/main-algorithms/knuth-bendix/kb-helpers.rst b/docs/source/main-algorithms/knuth-bendix/helpers.rst similarity index 65% rename from docs/source/main-algorithms/knuth-bendix/kb-helpers.rst rename to docs/source/main-algorithms/knuth-bendix/helpers.rst index ac43b114..66a8b8fb 100644 --- a/docs/source/main-algorithms/knuth-bendix/kb-helpers.rst +++ b/docs/source/main-algorithms/knuth-bendix/helpers.rst @@ -10,8 +10,8 @@ Knuth-Bendix helpers ==================== This page contains the documentation for various helper functions for -manipulating ``KnuthBendix`` objects. All such functions are contained in the -submodule ``libsemigroups_pybind11.knuth_bendix``. +manipulating :any:`KnuthBendixRewriteTrie` objects. All such functions are +contained in the submodule ``libsemigroups_pybind11.knuth_bendix``. .. seealso:: :py:class:`overlap` @@ -20,9 +20,9 @@ submodule ``libsemigroups_pybind11.knuth_bendix``. Contents -------- -.. .. currentmodule:: libsemigroups_pybind11.knuth_bendix +.. currentmodule:: libsemigroups_pybind11.knuth_bendix -.. .. autosummary:: +.. autosummary:: :nosignatures: by_overlap_length @@ -34,6 +34,7 @@ Contents Full API -------- -.. .. automodule:: libsemigroups_pybind11.knuth_bendix +.. automodule:: libsemigroups_pybind11.knuth_bendix :members: :imported-members: + :exclude-members: KnuthBendix diff --git a/docs/source/main-algorithms/knuth-bendix/index.rst b/docs/source/main-algorithms/knuth-bendix/index.rst index 42a8fde6..0ce23afe 100644 --- a/docs/source/main-algorithms/knuth-bendix/index.rst +++ b/docs/source/main-algorithms/knuth-bendix/index.rst @@ -7,13 +7,13 @@ Knuth-Bendix ============= - This page describes the functionality for the Knuth-Bendix procedure - in ``libsemigroups_pybind11``. +This page describes the functionality for the Knuth-Bendix procedure +in ``libsemigroups_pybind11``. .. toctree:: :maxdepth: 1 - knuth-bendix - kb-helpers - overlap \ No newline at end of file + class + helpers + nested diff --git a/docs/source/main-algorithms/knuth-bendix/knuth-bendix.rst b/docs/source/main-algorithms/knuth-bendix/knuth-bendix.rst deleted file mode 100644 index 3cec77e1..00000000 --- a/docs/source/main-algorithms/knuth-bendix/knuth-bendix.rst +++ /dev/null @@ -1,79 +0,0 @@ -.. 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. - -.. currentmodule:: _libsemigroups_pybind11 - -Knuth-Bendix -============ - -.. On this page we describe the functionality relating to the Knuth-Bendix - algorithm for semigroups and monoids that is available in - ``libsemigroups_pybind11``. This page contains a details of the methods - of the class :py:class:`KnuthBendix`. This class is used to represent a - `string rewriting system `_ defining a finitely presented - monoid or semigroup. - - .. doctest:: - - >>> from libsemigroups_pybind11 import KnuthBendix, Presentation, presentation, congruence_kind - >>> p = Presentation("abc") - >>> presentation.add_rule(p, "aaaa", "a") - >>> presentation.add_rule(p, "bbbb", "b") - >>> presentation.add_rule(p, "cccc", "c") - >>> presentation.add_rule(p, "abab", "aaa") - >>> presentation.add_rule(p, "bcbc", "bbb") - >>> kb = KnuthBendix(congruence_kind.twosided, p) - >>> not kb.confluent() - True - >>> kb.run() - >>> kb.number_of_active_rules() - 31 - >>> kb.confluent() - True - - Contents - -------- - .. autosummary:: - :nosignatures: - - ~KnuthBendixRewriteTrie - KnuthBendixRewriteTrie.active_rules - KnuthBendixRewriteTrie.add_pair - KnuthBendixRewriteTrie.batch_size - KnuthBendixRewriteTrie.check_confluence_interval - KnuthBendixRewriteTrie.confluent - KnuthBendixRewriteTrie.confluent_known - KnuthBendixRewriteTrie.contains - KnuthBendixRewriteTrie.current_state - KnuthBendixRewriteTrie.equal_to - KnuthBendixRewriteTrie.generating_pairs - KnuthBendixRewriteTrie.gilman_graph - KnuthBendixRewriteTrie.gilman_graph_node_labels - KnuthBendixRewriteTrie.kind - KnuthBendixRewriteTrie.max_overlap - KnuthBendixRewriteTrie.max_rules - KnuthBendixRewriteTrie.normal_form - KnuthBendixRewriteTrie.number_of_active_rules - KnuthBendixRewriteTrie.number_of_classes - KnuthBendixRewriteTrie.number_of_generating_pairs - KnuthBendixRewriteTrie.number_of_inactive_rules - KnuthBendixRewriteTrie.overlap_policy - KnuthBendixRewriteTrie.presentation - KnuthBendixRewriteTrie.rewrite - KnuthBendixRewriteTrie.total_rules - - Full API - -------- - - .. autoclass:: KnuthBendixRewriteTrie - :members: - - Methods inherited from Runner - ----------------------------- - - .. autoclass:: Runner - :members: - :noindex: diff --git a/docs/source/main-algorithms/knuth-bendix/nested.rst b/docs/source/main-algorithms/knuth-bendix/nested.rst new file mode 100644 index 00000000..fdded7eb --- /dev/null +++ b/docs/source/main-algorithms/knuth-bendix/nested.rst @@ -0,0 +1,18 @@ +.. 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. + +.. currentmodule:: libsemigroups_pybind11 + +The options nested class +======================== + +This page contains documentation for the nested class +:any:`KnuthBendixRewriteTrie.options` which holds various values that can be used +to control the behaviour of Knuth-Bendix. + +.. autoclass:: _libsemigroups_pybind11::KnuthBendixRewriteTrie.options + :members: + :exclude-members: name diff --git a/docs/source/main-algorithms/knuth-bendix/overlap.rst b/docs/source/main-algorithms/knuth-bendix/overlap.rst deleted file mode 100644 index 1c24b0f6..00000000 --- a/docs/source/main-algorithms/knuth-bendix/overlap.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. Copyright (c) 2023-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. - -.. currentmodule:: libsemigroups_pybind11 - -Overlap -======= - -TODO: An explanation of overlaps - -.. autoclass:: overlap \ No newline at end of file diff --git a/docs/source/main-algorithms/todd-coxeter/class/common.rst b/docs/source/main-algorithms/todd-coxeter/class/common.rst index 2ddae075..63993fe0 100644 --- a/docs/source/main-algorithms/todd-coxeter/class/common.rst +++ b/docs/source/main-algorithms/todd-coxeter/class/common.rst @@ -11,7 +11,7 @@ Common methods This page contains documentation of the methods of :any:`ToddCoxeter` that are implemented in all of the classes :any:`Congruence`, :any:`Kambites`, -:any:`KnuthBendix`, and :any:`ToddCoxeter`. +:any:`KnuthBendixRewriteTrie`, and :any:`ToddCoxeter`. .. automethod:: ToddCoxeter.add_generating_pair .. automethod:: ToddCoxeter.contains diff --git a/docs/source/main-algorithms/todd-coxeter/class/index_to_word.rst b/docs/source/main-algorithms/todd-coxeter/class/index_to_word.rst index 28778b01..0042e024 100644 --- a/docs/source/main-algorithms/todd-coxeter/class/index_to_word.rst +++ b/docs/source/main-algorithms/todd-coxeter/class/index_to_word.rst @@ -4,7 +4,7 @@ The full license is in the file LICENSE, distributed with this software. -.. currentmodule:: _libsemigroups_pybind11 +.. currentmodule:: libsemigroups_pybind11.todd_coxeter Class index to word =================== @@ -14,6 +14,4 @@ can be used to convert the index of a congruence class to a representative word belonging to that congruence class. .. automethod:: ToddCoxeter.current_word_of -.. automethod:: ToddCoxeter.current_str_of .. automethod:: ToddCoxeter.word_of -.. automethod:: ToddCoxeter.str_of diff --git a/docs/source/main-algorithms/todd-coxeter/helpers.rst b/docs/source/main-algorithms/todd-coxeter/helpers.rst index 42864b3e..4bd7188b 100644 --- a/docs/source/main-algorithms/todd-coxeter/helpers.rst +++ b/docs/source/main-algorithms/todd-coxeter/helpers.rst @@ -18,11 +18,9 @@ Contents .. autosummary:: :nosignatures: - str_class_by_index - word_class_by_index + class_by_index class_of - word_normal_forms - str_normal_forms + normal_forms is_non_trivial non_trivial_classes partition diff --git a/etc/replace-strings-in-doc.py b/etc/replace-strings-in-doc.py index efde2e5f..93d66750 100755 --- a/etc/replace-strings-in-doc.py +++ b/etc/replace-strings-in-doc.py @@ -6,9 +6,7 @@ def dict_sub(replacements, string): """replacements has the form {"regex1": "replacement", "regex2": "replacement2", ...}""" - global_expression = re.compile( - "|".join("(" + x + ")" for x in replacements) - ) + global_expression = re.compile("|".join("(" + x + ")" for x in replacements)) replacements_by_group = {} group = 1 for expr, replacement in replacements.items(): @@ -42,7 +40,7 @@ def dive(path): "PresentationStrings": "Presentation", r"_libsemigroups_pybind11.": "", "libsemigroups::Presentation, std::allocator > >": "Presentation", - "RightActionPPerm16List": "Action", + "RightActionPPerm1List": "Action", "libsemigroups::BMat8": "BMat8", "libsemigroups_pybind11.bmat8": "bmat8", "libsemigroups_pybind11.knuth_bendix": "knuth_bendix", diff --git a/libsemigroups_pybind11/__init__.py b/libsemigroups_pybind11/__init__.py index 78269d83..8a410762 100644 --- a/libsemigroups_pybind11/__init__.py +++ b/libsemigroups_pybind11/__init__.py @@ -47,7 +47,6 @@ lexicographical_compare, recursive_path_compare, shortlex_compare, - overlap, to_presentation, to_inverse_presentation, LibsemigroupsError, @@ -82,7 +81,7 @@ from .adapters import ImageRightAction, ImageLeftAction -# from .knuth_bendix import KnuthBendix +from .knuth_bendix import KnuthBendix from .matrix import _Matrix as Matrix, _MatrixKind as MatrixKind from .presentation import Presentation, InversePresentation from .transf import ( diff --git a/libsemigroups_pybind11/detail/decorators.py b/libsemigroups_pybind11/detail/decorators.py index 207ce76e..778f57f1 100644 --- a/libsemigroups_pybind11/detail/decorators.py +++ b/libsemigroups_pybind11/detail/decorators.py @@ -77,3 +77,45 @@ def wrapper(self, *args, **kwargs): return result return wrapper + + +def template_params_as_kwargs(**kwargs_map): + """ + Usage example: + + @template_params_as_kwargs( + Word={str: _str_normal_forms, List[int]: _word_normal_forms} + ) + def foo(**kwargs): + pass + + will call _str_normal_forms(*args) on calls to foo(Word = str) and call + _word_normal_forms on calls to foo(Word = List[int]). + + Currently only works with a single kwarg + """ + + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + if len(kwargs) != len(kwargs_map): + raise TypeError( + f"expected {len(kwargs_map)} keyword argument" + f"{'s'[:len(kwargs_map)^1]}, but found {len(kwargs)}" + ) + if kwargs_map.keys() != kwargs.keys(): + raise TypeError( + f"expected keyword arguments {tuple(kwargs_map.keys())}, " + f"but found {tuple(kwargs.keys())}" + ) + for key, val in kwargs.items(): + if val in kwargs_map[key]: + return kwargs_map[key][val](*args) + raise TypeError( + f'expected the value of the keyword argument "{key}" to belong in ' + f"{kwargs_map[key].keys()}, but found {val}" + ) + + return wrapper + + return decorator diff --git a/libsemigroups_pybind11/knuth_bendix.py b/libsemigroups_pybind11/knuth_bendix.py new file mode 100644 index 00000000..51429f4e --- /dev/null +++ b/libsemigroups_pybind11/knuth_bendix.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2024, J. D. Mitchell, Joseph Edwards +# +# Distributed under the terms of the GPL license version 3. +# +# The full license is in the file LICENSE, distributed with this software. + +# pylint: disable=no-name-in-module, unused-import, protected-access, +# pylint: disable=missing-function-docstring, line-too-long + +""" +This package provides the user-facing python part of libsemigroups_pybind11 for +the KnuthBendix class from libsemigroups. +""" + +from typing import List, Iterator, Union + +from _libsemigroups_pybind11 import ( + KnuthBendixRewriteFromLeft as _KnuthBendixRewriteFromLeft, + KnuthBendixRewriteTrie as _KnuthBendixRewriteTrie, + PresentationStrings as _PresentationStrings, + PresentationWords as _PresentationWords, + congruence_kind as _congruence_kind, + knuth_bendix_by_overlap_length as by_overlap_length, + knuth_bendix_str_normal_forms as _knuth_bendix_str_normal_forms, + knuth_bendix_word_normal_forms as _knuth_bendix_word_normal_forms, + knuth_bendix_str_non_trivial_classes as _knuth_bendix_str_non_trivial_classes, + knuth_bendix_word_non_trivial_classes as _knuth_bendix_word_non_trivial_classes, + knuth_bendix_redundant_rule as redundant_rule, + knuth_bendix_is_reduced as is_reduced, +) + +from .detail.decorators import ( + may_return_positive_infinity as _may_return_positive_infinity, + template_params_as_kwargs as _template_params_as_kwargs, +) + +_Presentation = (_PresentationStrings, _PresentationWords) +_KnuthBendix = (_KnuthBendixRewriteFromLeft, _KnuthBendixRewriteTrie) + +for KB in _KnuthBendix: + KB.number_of_classes = _may_return_positive_infinity(KB._number_of_classes) + KB.number_of_classes.__doc__ = "\n".join( + KB._number_of_classes.__doc__.split("\n")[1:] + ) + + +def KnuthBendix(*args, rewriter="RewriteTrie"): # pylint: disable=invalid-name + """ + Construct a KnuthBendix instance of the type specified by its arguments + """ + if len(args) not in (0, 2): + raise TypeError(f"expected 0 or 2 positional arguments, found {len(args)}") + + if len(args) == 2 and not isinstance(args[0], _congruence_kind): + raise TypeError( + ( + "the 1st positional argument must be congruence_kind, " + f"found ({type(args[0])})" + ) + ) + + if len(args) == 2 and not isinstance(args[1], _Presentation): + raise TypeError( + ( + "the 2nd positional argument must be presentation, " + f"found ({type(args[1])})" + ) + ) + + if rewriter == "RewriteFromLeft": + result = _KnuthBendixRewriteFromLeft(*args) + elif rewriter == "RewriteTrie": + result = _KnuthBendixRewriteTrie(*args) + else: + raise TypeError( + ( + f'expected the rewriter kwarg to be either "RewriteTrie" or ' + f'"RewriteFromLeft", but found {rewriter}' + ) + ) + + return result + + +KnuthBendix.options = _KnuthBendixRewriteTrie.options + + +# The next function (non_trivial_classes) is documented here not in the cpp +# file because we add the additional kwarg Word. +@_template_params_as_kwargs( + Word={ + str: _knuth_bendix_str_non_trivial_classes, + List[int]: _knuth_bendix_word_non_trivial_classes, + } +) +def non_trivial_classes( + kb1: KnuthBendix, + kb2: KnuthBendix, + **kwargs, # pylint: disable=unused-argument +) -> List[List[Union[str, List[int]]]]: + r""" + Find the non-trivial classes of the quotient of one KnuthBendix instance in + another. + + This function returns the classes with size at least :math:`2` in the normal + forms of *kb1* in *kb2* (the greater congruence, with fewer classes). This + function triggers a full enumeration of both *kb1* and *kb2*. Note that this + function does **not** compute the normal forms of *kb1* and try to compute the + partition of these induced by *kb2*, before filtering out the classes of + size :math:`1`. In particular, it is possible to compute the non-trivial + classes of *kb2* in *kb1* if there are only finitely many finite such + classes, regardless of whether or not *kb1* or *kb2* has infinitely many + classes. + + :param kb1: the first KnuthBendix instance. + :type kb1: KnuthBendixRewriteTrie + + :param kb2: the second KnuthBendix instance. + :type kb2: KnuthBendixRewriteTrie + + :Keyword Arguments: + * *Word* (``type``) -- type of the output words (must be ``str`` or ``List[int]``). + + :returns: The non-trivial classes of *kb1* in *kb2*. + :rtype: List[List[str | List[int]]] + + :raises LibsemigroupsError: + if *kb2* has infinitely many classes and *kb1* has finitely many + classes (so that there is at least one infinite non-trivial class). + + :raises LibsemigroupsError: + if the alphabets of the presentations of *kb1* and *kb2* are not equal. + + :raises LibsemigroupsError: + if the :any:`KnuthBendixRewriteTrie.gilman_graph` of *kb2* has fewer nodes + than that of *kb1*. + + :raises TypeError: + if the keyword argument *Word* is not present, any other keyword + argument is present, or is present but has value other than ``str`` or + ``List[int]``. + + .. doctest:: + + >>> from libsemigroups_pybind11 import (knuth_bendix, presentation, + ... Presentation, congruence_kind, KnuthBendix) + >>> from typing import List + >>> p = Presentation("abc") + >>> p.rules = ["ab", "ba", "ac", "ca", "aa", "a", "ac", "a", "ca", + ... "a", "bc", "cb", "bbb", "b", "bc", "b", "cb", "b"] + >>> kb1 = KnuthBendix(congruence_kind.twosided, p) + >>> presentation.add_rule(p, "a", "b") + >>> kb2 = KnuthBendix(congruence_kind.twosided, p) + >>> knuth_bendix.non_trivial_classes(kb1, kb2, Word=str) + [['b', 'ab', 'bb', 'abb', 'a']] + >>> knuth_bendix.non_trivial_classes(kb1, kb2, Word=List[int]) + [[[98], [97, 98], [98, 98], [97, 98, 98], [97]]] + >>> p = Presentation([0, 1, 2]) + >>> p.rules = [[0, 1], [1, 0], [0, 2], [2, 0], [0, 0], [0], [0, 2], [0], [2, 0], + ... [0], [1, 2], [2, 1], [1, 1, 1], [1], [1, 2], [1], [2, 1], [1]] + >>> kb1 = KnuthBendix(congruence_kind.twosided, p) + >>> presentation.add_rule(p, [0], [1]) + >>> kb2 = KnuthBendix(congruence_kind.twosided, p) + >>> knuth_bendix.non_trivial_classes(kb1, kb2, Word=List[int]) + [[[1], [0, 1], [1, 1], [0, 1, 1], [0]]] + """ + + +# The next function (normal_forms) is documented here not in the cpp +# file because we add the additional kwarg Word. +@_template_params_as_kwargs( + Word={ + str: _knuth_bendix_str_normal_forms, + List[int]: _knuth_bendix_word_normal_forms, + } +) +def normal_forms(kb: KnuthBendix, **kwargs) -> Iterator[Union[str, List[int]]]: # pylint: disable=unused-argument + r""" + Returns an iterator yielding normal forms. + + This function returns an iterator yielding normal forms of the classes of + the congruence represented by an instance of :any:`KnuthBendixRewriteTrie`. The + order of the classes, and the normal form that is returned, are controlled by + the reduction order used to construct *kb*. This function triggers a full + enumeration of *kb*. + + :param kb: the KnuthBendix instance. + :type kb: KnuthBendixRewriteTrie + + :Keyword Arguments: + * *Word* (``type``) -- type of the output words (must be ``str`` or ``List[int]``). + + :returns: An iterator. + :rtype: Iterator[str | List[int]] + + :raises TypeError: + if the keyword argument *Word* is not present, any other keyword + argument is present, or is present but has value other than ``str`` or + ``List[int]``. + + .. doctest:: + + >>> from libsemigroups_pybind11 import (KnuthBendix, Presentation, + ... presentation, congruence_kind) + >>> from typing import List + >>> p = Presentation("abc") + >>> presentation.add_rule(p, "aaaa", "a") + >>> presentation.add_rule(p, "bbbb", "b") + >>> presentation.add_rule(p, "cccc", "c") + >>> presentation.add_rule(p, "abab", "aaa") + >>> presentation.add_rule(p, "bcbc", "bbb") + >>> kb = KnuthBendix(congruence_kind.twosided, p) + >>> kb.number_of_classes() + +∞ + >>> list(knuth_bendix.normal_forms(kb, Word=str).min(1).max(3)) + ['a', 'b', 'c', 'aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc'] + >>> list(knuth_bendix.normal_forms(kb, Word=List[int]).min(1).max(3)) + [[97], [98], [99], [97, 97], [97, 98], [97, 99], [98, 97], [98, 98], [98, 99], [99, 97], [99, 98], [99, 99]] + """ diff --git a/libsemigroups_pybind11/presentation.py b/libsemigroups_pybind11/presentation.py index 5a401d26..c1e8ebf9 100644 --- a/libsemigroups_pybind11/presentation.py +++ b/libsemigroups_pybind11/presentation.py @@ -25,6 +25,7 @@ add_rules, add_zero_rules, are_rules_sorted, + # TODO(0) balance? change_alphabet, contains_rule, first_unused_letter, @@ -74,9 +75,7 @@ def Presentation(arg): "using copy.copy" ) else: - raise TypeError( - "expected the argument to be a string or a list of ints" - ) + raise TypeError("expected the argument to be a string or a list of ints") return result @@ -108,9 +107,7 @@ def InversePresentation(arg): elif isinstance(arg, list) and all(isinstance(x, int) for x in arg): result = __InversePresentationWords() result.alphabet(arg) - elif isinstance( - arg, (__InversePresentationStrings, __InversePresentationWords) - ): + elif isinstance(arg, (__InversePresentationStrings, __InversePresentationWords)): raise TypeError( "expected the argument to be a presentation, a string or a list of " "ints; received an InversePresentation. If you are trying to copy " @@ -118,7 +115,6 @@ def InversePresentation(arg): ) else: raise TypeError( - "expected the argument to be a Presentation, string, or list of " - "ints" + "expected the argument to be a Presentation, string, or list of ints" ) return result diff --git a/libsemigroups_pybind11/todd_coxeter.py b/libsemigroups_pybind11/todd_coxeter.py index 9ce46ecf..92ce2819 100644 --- a/libsemigroups_pybind11/todd_coxeter.py +++ b/libsemigroups_pybind11/todd_coxeter.py @@ -9,15 +9,15 @@ # pylint: disable=no-name-in-module, invalid-name, missing-function-docstring # pylint: disable=unused-import, missing-module-docstring, protected-access -from typing import Union +from typing import Union, List, Iterator from _libsemigroups_pybind11 import ( ToddCoxeter, PositiveInfinity, - todd_coxeter_str_normal_forms as str_normal_forms, - todd_coxeter_word_normal_forms as word_normal_forms, - word_class_by_index, - str_class_by_index, + todd_coxeter_str_normal_forms as _str_normal_forms, + todd_coxeter_word_normal_forms as _word_normal_forms, + _word_class_by_index, + _str_class_by_index, class_of, todd_coxeter_is_non_trivial as is_non_trivial, todd_coxeter_partition as partition, @@ -29,8 +29,14 @@ from .detail.decorators import ( may_return_positive_infinity as _may_return_positive_infinity, may_return_undefined as _may_return_undefined, + template_params_as_kwargs as _template_params_as_kwargs, ) + +def noop(): + pass + + ToddCoxeter.number_of_classes = _may_return_positive_infinity( ToddCoxeter._number_of_classes ) @@ -38,14 +44,149 @@ ToddCoxeter._number_of_classes.__doc__.split("\n")[1:] ) -ToddCoxeter.current_index_of = _may_return_undefined( - ToddCoxeter._current_index_of -) +ToddCoxeter.current_index_of = _may_return_undefined(ToddCoxeter._current_index_of) ToddCoxeter.current_index_of.__doc__ = "\n".join( ToddCoxeter._current_index_of.__doc__.split("\n")[1:] ) +ToddCoxeter.current_word_of = _template_params_as_kwargs( + Word={ + str: ToddCoxeter._current_str_of, + List[int]: ToddCoxeter._current_word_of, + } +)(noop) + + +ToddCoxeter.current_word_of.__doc__ = """ +:sig=(i: int, **kwargs) -> 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*. +No enumeration is triggered by calls to this function, but +:any:`current_word_graph` is standardized (using :any:`Order.shortlex`) if it +is not already standardized. The output word is obtained by following a path in +:any:`current_spanning_tree` from the node corresponding to index *i* back to +the root of that tree. + +:param i: the index of the class. +:type i: int + +:Keyword Arguments: + * *Word* (``type``) -- type of the output words (must be ``str`` or ``List[int]``). + +:returns: The word representing the *i*-th class. +:rtype: List[int] | str + +:raises LibsemigroupsError: if *i* is out of bounds. + +:raises TypeError: + if the keyword argument *Word* is not present, any other keyword + argument is present, or is present but has value other than ``str`` or + ``List[int]``. +""" + +ToddCoxeter.word_of = _template_params_as_kwargs( + Word={ + str: ToddCoxeter._str_of, + List[int]: ToddCoxeter._word_of, + } +)(noop) + +ToddCoxeter.word_of.__doc__ = """ +:sig=(i: int, **kwargs) -> List[int] | str: +Returns a word representing a class with given index. + +This function returns the word representing the class with index *i*. A full +enumeration is triggered by calls to this function. The output word is obtained +by following a path in :any:`current_spanning_tree` from the node corresponding +to index *i* back to the root of that tree. + +:param i: the index of the class. +:type i: int + +:Keyword Arguments: + * *Word* (``type``) -- type of the output words (must be ``str`` or ``List[int]``). + +:returns: The word representing the *i*-th class. +:rtype: List[int] + +:raises LibsemigroupsError: if *i* is out of bounds. + +:raises TypeError: + if the keyword argument *Word* is not present, any other keyword + argument is present, or is present but has value other than ``str`` or + ``List[int]``. +""" + + +# The next function (normal_forms) is documented here not in the cpp +# file because we add the additional kwarg Word. +@_template_params_as_kwargs( + Word={str: _str_normal_forms, List[int]: _word_normal_forms} +) +def normal_forms(kb: ToddCoxeter, **kwargs) -> Iterator[Union[str, List[int]]]: # pylint: disable=unused-argument + r""" + Returns an iterator yielding normal forms. + + This function returns an iterator yielding normal forms of the classes of + the congruence represented by an instance of :any:`ToddCoxeter`. The order of + the classes, and the normal forms, that are returned are controlled by + :any:`ToddCoxeter.standardize`. This function triggers a full enumeration of + ``tc``. + + :param tc: the ToddCoxeter instance. + :type tc: ToddCoxeter + + :Keyword Arguments: + * *Word* (``type``) -- type of the output words (must be ``str`` or ``List[int]``). + + :returns: An iterator yielding normal forms. + :rtype: Iterator[str | List[int]] + + :raises TypeError: + if the keyword argument *Word* is not present, any other keyword + argument is present, or is present but has value other than ``str`` or + ``List[int]``. + """ + + +# The next function (class_by_index) is documented here not in the cpp +# file because we add the additional kwarg Word. +@_template_params_as_kwargs( + Word={str: _str_class_by_index, List[int]: _word_class_by_index} +) +def class_by_index(kb: ToddCoxeter, **kwargs) -> Iterator[Union[str, List[int]]]: # pylint: disable=unused-argument + """ + Returns an iterator yielding every word ``List[int]`` or ``str`` in the + congruence class with given index. + + This function returns an iterator yielding every word belonging to the + class with index *n* in the congruence represented by the :any:`ToddCoxeter` + instance *tc*. Calls to this function trigger a full enumeration of *tc*. + + :param tc: the ToddCoxeter instance. + :type tc: ToddCoxeter + + :param n: the index of the class. + :type n: int + + :Keyword Arguments: + * *Word* (``type``) -- type of the output words (must be ``str`` or ``List[int]``). + + :returns: A iterator yielding the class with index *n*. + :rtype: Iterator[List[int]] + + :raises LibsemigroupsError: + if *n* is greater than or equal to ``tc.number_of_classes()``. + + :raises TypeError: + if the keyword argument *Word* is not present, any other keyword + argument is present, or is present but has value other than ``str`` or + ``List[int]``. + """ + + # def fancy_dot(tc: ToddCoxeter) -> _Dot: # dot = word_graph.dot(tc.word_graph()) # offset = 0 if tc.presentation().contains_empty_word() else 1 diff --git a/src/aho-corasick.cpp b/src/aho-corasick.cpp index 1d27fbe2..51418758 100644 --- a/src/aho-corasick.cpp +++ b/src/aho-corasick.cpp @@ -52,7 +52,7 @@ with the Aho-Corasick dictionary searching algorithm. An introduction to this algorithm can be found `here `_. The implementation of :any:`AhoCorasick` uses two different types of node; -*active* and *inactive* . An active node is a node that is currently a node +*active* and *inactive*. An active node is a node that is currently a node in the trie. An inactive node is a node that used to be part of the trie, but has since been removed. It may later become active again after being reinitialised, and exists as a way of minimising how frequently memory needs diff --git a/src/bipart.cpp b/src/bipart.cpp index 82d7aa15..4edb01b9 100644 --- a/src/bipart.cpp +++ b/src/bipart.cpp @@ -501,7 +501,7 @@ returned vector is ``True`` if the block with index ``i`` is transverse and Return the identity bipartition with the same degree as the given bipartition. The *identity bipartition* of degree :math:`n` has blocks :math:`\{i, -i\}` for -all :math:`i\in \{0, \ldots, n - 1\}` . This function returns a new identity +all :math:`i\in \{0, \ldots, n - 1\}`. This function returns a new identity bipartition of degree equal to the degree of ``self``. :param f: a bipartition diff --git a/src/bmat8.cpp b/src/bmat8.cpp index 342b4d75..6a2530ad 100644 --- a/src/bmat8.cpp +++ b/src/bmat8.cpp @@ -497,13 +497,15 @@ Returns the size of the column space of a :any:`BMat8`. 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 +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** :rtype: int + :complexity: Constant. .. doctest:: diff --git a/src/cong-intf.cpp b/src/cong-intf.cpp index 4e7c2742..4bc866d8 100644 --- a/src/cong-intf.cpp +++ b/src/cong-intf.cpp @@ -42,7 +42,7 @@ data that are common to all its derived classes. These classes are: * :any:`Congruence` * :any:`Kambites` -* :any:`KnuthBendix` +* :py:class:`KnuthBendixRewriteTrie` * :any:`ToddCoxeter` )pbdoc"); thing.def( diff --git a/src/forest.cpp b/src/forest.cpp index 4bfdc83a..76a498d8 100644 --- a/src/forest.cpp +++ b/src/forest.cpp @@ -211,8 +211,8 @@ Returns the parent of a node. thing.def("parents", &Forest::parents, R"pbdoc( -Returns a list of parents in the :any:`Forest` . The value in position ``i`` of -this list is the parent of node ``i`` . If the parent equals :any:`UNDEFINED`, +Returns a list of parents in the :any:`Forest`. The value in position ``i`` of +this list is the parent of node ``i``. If the parent equals :any:`UNDEFINED`, then node ``i`` is a root node. :returns: diff --git a/src/froidure-pin-base.cpp b/src/froidure-pin-base.cpp index 91573440..dea8f803 100644 --- a/src/froidure-pin-base.cpp +++ b/src/froidure-pin-base.cpp @@ -62,7 +62,7 @@ This function returns the minimum number of elements enumerated in any call to Set a new value for the batch size. The *batch size* is the number of new elements to be found by any call to -:any:`Runner.run` . This is used by, for example, :any:`FroidurePinPBR.position` so +:any:`Runner.run`. This is used by, for example, :any:`FroidurePinPBR.position` so that it is possible to find the position of an element after only partially enumerating the semigroup.The default value of the batch size is ``8192``. @@ -358,7 +358,7 @@ Returns the number of elements so far enumerated with length in a given range. This function returns the number of elements that have been enumerated so -far with length in the range :math:`[min, max)` . This function does not +far with length in the range :math:`[min, max)`. This function does not trigger any enumeration. :param min: the minimum length. diff --git a/src/froidure-pin.cpp b/src/froidure-pin.cpp index bf90ea7f..c0c9b010 100644 --- a/src/froidure-pin.cpp +++ b/src/froidure-pin.cpp @@ -193,7 +193,7 @@ See :any:`add_generator` for a detailed description. Find the position of an element with no enumeration. This function returns the position of the element *x* in the semigroup if it -is already known to belong to the semigroup or :any:`UNDEFINED` . This +is already known to belong to the semigroup or :any:`UNDEFINED`. This function finds the position of the element *x* if it is already known to belong to a :any:`FroidurePinPBR` instance, and :any:`UNDEFINED` if not. If a :any:`FroidurePinPBR` instance is not yet fully enumerated, then this function diff --git a/src/kambites.cpp b/src/kambites.cpp index bcd6b6d0..f04321c8 100644 --- a/src/kambites.cpp +++ b/src/kambites.cpp @@ -47,10 +47,9 @@ Construct from :any:`congruence_kind` and :any:`PresentationStrings`. This function constructs a :any:`Kambites` instance representing a congruence of kind *knd* over the semigroup or monoid defined by the presentation *p*. :any:`Kambites` instances can only be used to compute two-sided congruences, -and so the first parameter *knd* must always be -``congruence_kind.twosided``. The parameter *knd* is included for -uniformity of interface between with :any:`KnuthBendix`, :any:`Kambites`, and -:any:`Congruence`. +and so the first parameter *knd* must always be ``congruence_kind.twosided``. +The parameter *knd* is included for uniformity of interface between with +:any:`KnuthBendixRewriteTrie`, :any:`Kambites`, and :any:`Congruence`. :param knd: the kind (onesided or twosided) of the congruence. :type knd: congruence_kind @@ -83,10 +82,10 @@ Add a generating pair. This function adds a generating pair to the congruence represented by a :any:`Kambites` instance. -:param u: the first item in the pair. +:param u: the first word. :type u: List[int] | str -:param v: the second item in the pair. +:param v: the second word. :type v: List[int] | str :raises LibsemigroupsError: @@ -119,10 +118,10 @@ Check containment of a pair of words. This function checks whether or not the words *u* and *v* are contained in the congruence represented by a :any:`Kambites` instance. -:param u: the first item in the pair. +:param u: the first word. :type u: List[int] | str -:param v: the second item in the pair. +:param v: the second word. :type v: List[int] | str :returns: Whether or not the pair belongs to the congruence. @@ -158,10 +157,10 @@ be contained in the congruence represented by a :any:`Kambites` instance. This function performs no enumeration, so it is possible for the words to be contained in the congruence, but that this is not currently known. -:param u: the first item in the pair. +:param u: the first word. :type u: List[int] | str -:param v: the second item in the pair. +:param v: the second word. :type v: List[int] | str :returns: @@ -246,7 +245,7 @@ this lexicographically least word always exists. void reduce_no_run(py::class_>& thing) { thing.def( "reduce_no_run", - [](Kambites& self, word_type const& w) { + [](Kambites& self, OtherWord const& w) { return kambites::reduce_no_run(self, w); }, py::arg("w"), @@ -512,7 +511,7 @@ of the words in the list *words* induced by the :any:`Kambites` instance *k*. Partition a list of words. This function returns the classes in the partition of the words in the input -list *words* induced by the :any:`Kambites` instance *k* . This function +list *words* induced by the :any:`Kambites` instance *k*. This function triggers a full enumeration of *k*. :param k: the :any:`Kambites` instance. diff --git a/src/knuth-bendix.cpp b/src/knuth-bendix.cpp new file mode 100644 index 00000000..93423dac --- /dev/null +++ b/src/knuth-bendix.cpp @@ -0,0 +1,936 @@ +// libsemigroups - C++ library for semigroups and monoids +// Copyright (C) 2021-2024 James D. Mitchell, Maria Tsalakou +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// C std headers.... +#include // for size_t + +// C++ stl headers.... +#include // for nanoseconds + +// libsemigroups.... + +#include // for KnuthBendix, KnuthBendix::option... +#include // for is_obviously_infinite +#include // for Runner +#include // for word_type, letter_type +#include // for WordGraph + +// pybind11.... +#include +#include +#include // for class_, make_iterator, enum_, init +#include // for py::str +#include + +// libsemigroups_pybind11.... +#include "main.hpp" // for init_knuth_bendix + +namespace py = pybind11; + +namespace libsemigroups { + + namespace { + template + void reduce_no_run( + py::class_, CongruenceInterface>& thing) { + thing.def( + "reduce_no_run", + [](KnuthBendix& self, Word const& w) { + return knuth_bendix::reduce_no_run(self, w); + }, + py::arg("w"), + R"pbdoc( +:sig=(self: KnuthBendix, w: List[int] | str) -> List[int] | str: +:only-document-once: + +Reduce a word. + +Rewrites the word *w* according to the current rules in the :py:class:`KnuthBendixRewriteTrie` +instance. + +:param w: the input word. +:type w: List[int] | str + +:raises LibsemigroupsError: + if any of the values in *w* is out of range, i.e. they do not belong to + ``presentation().alphabet()`` and :any:`PresentationStrings.validate_word` + raises.)pbdoc"); + } + + template + void reduce(py::class_, CongruenceInterface>& thing) { + thing.def( + "reduce", + [](KnuthBendix& self, Word const& w) { + return knuth_bendix::reduce_no_run(self, w); + }, + py::arg("w"), + R"pbdoc( +:sig=(self: KnuthBendix, w: List[int] | str) -> List[int] | str: +:only-document-once: + +Reduce a word. + +This function triggers a full enumeration and then reduces the word *w* +according to the rules in the :py:class:`KnuthBendixRewriteTrie` +instance. + +:param w: the input word. +:type w: List[int] | str + +:raises LibsemigroupsError: + if any of the values in *w* is out of range, i.e. they do not belong to + ``presentation().alphabet()`` and :any:`PresentationStrings.validate_word` + raises. +)pbdoc"); + } + + template + void + contains(py::class_, CongruenceInterface>& thing) { + thing.def( + "contains", + [](KnuthBendix& self, Word const& u, Word const& v) { + return knuth_bendix::contains(self, u, v); + }, + py::arg("u"), + py::arg("v"), + R"pbdoc( +:sig=(self: KnuthBendix, u: List[int] | str, v: List[int] | str) -> bool: +:only-document-once: + +Check containment of a pair of words. + +This function checks whether or not the words *u* and *v* are contained in the +congruence represented by a :py:class:`KnuthBendixRewriteTrie` instance. + +:param u: the first word. +:type u: List[int] | str + +:param v: the second word. +:type v: List[int] | str + +:returns: Whether or not the pair belongs to the congruence. +:rtype: bool + +:raises LibsemigroupsError: + if any of the values in *u* or *v* is out of range, i.e. they do not belong + to ``presentation().alphabet()`` and :any:`PresentationStrings.validate_word` + raises. +)pbdoc"); + } + + template + void currently_contains( + py::class_, CongruenceInterface>& thing) { + thing.def( + "currently_contains", + [](KnuthBendix const& self, Word const& u, Word const& v) { + return knuth_bendix::currently_contains(self, u, v); + }, + py::arg("u"), + py::arg("v"), + R"pbdoc( +:sig=(self: KnuthBendix, u: List[int] | str, v: List[int] | str) -> bool: +:only-document-once: + +Check whether a pair of words is already known to belong to the congruence. + +This function checks whether or not the words *u* and *v* are already known to +be contained in the congruence represented by a :py:class:`KnuthBendixRewriteTrie` instance. +This function performs no enumeration, so it is possible for the words to be +contained in the congruence, but that this is not currently known. + +:param u: the first word. +:type u: List[int] | str + +:param v: the second word. +:type v: List[int] | str + +:returns: + * :any:`tril.true` if the words are known to belong to the congruence; + * :any:`tril.false` if the words are known to not belong to the congruence; + * :any:`tril.unknown` otherwise. +:rtype: tril + +:raises LibsemigroupsError: + if any of the values in *u* or *v* is out of range, i.e. they do not belong + to ``presentation().alphabet()`` and :any:`PresentationStrings.validate_word` + raises. + +:raises LibsemigroupsError: + if :any:`small_overlap_class` is not at least :math:`4`. +)pbdoc"); + } + +#define CONCAT(first, second) first##second +#define CONCAT3(first, second, third) first##second##third + +#define STRINGIFY(x) #x +#define TO_STRING(x) STRINGIFY(x) + + template + void bind_normal_form_range(py::module& m, char const* name) { + using NormalFormRange + = detail::KnuthBendixNormalFormRange; + py::class_ thing1(m, name); + + thing1.def("__repr__", [](NormalFormRange const& nfr) { + return to_human_readable_repr(nfr); + }); + + thing1.def("__copy__", [](NormalFormRange const& nfr) { + return NormalFormRange(nfr); + }); + // __len__ is not allowed to return anything other than an int, hence + // __len__ and count don't have the same behaviour. + thing1.def("__len__", [](NormalFormRange const& nfr) { + auto result = nfr.count(); + if (result == POSITIVE_INFINITY) { + return py::module_::import("sys").attr("maxsize").cast(); + } + return result; + }); + + thing1.def("__iter__", [](NormalFormRange const& nfr) { + return py::make_iterator(rx::begin(nfr), rx::end(nfr)); + }); + + thing1.def("at_end", [](NormalFormRange& nfr) { return nfr.at_end(); }); + thing1.def("count", [](NormalFormRange& nfr) { return nfr.count(); }); + + thing1.def("get", [](NormalFormRange& nfr) { return nfr.get(); }); + + thing1.def("max", [](NormalFormRange const& self) { return self.max(); }); + + thing1.def("max", + [](NormalFormRange& self, PositiveInfinity const& val) + -> NormalFormRange& { return self.max(val); }); + thing1.def("max", + [](NormalFormRange& self, size_t val) -> NormalFormRange& { + return self.max(val); + }); + thing1.def("min", [](NormalFormRange const& self) { return self.min(); }); + thing1.def("min", + [](NormalFormRange& self, size_t val) -> NormalFormRange& { + return self.min(val); + }); + thing1.def("next", [](NormalFormRange& nfr) { nfr.next(); }); + } + + template + void bind_knuth_bendix(py::module& m, std::string const& name) { + py::class_, CongruenceInterface> kb(m, + name.c_str(), + R"pbdoc( +Class containing an implementation of the Knuth-Bendix Algorithm. + +On this page we describe the functionality relating to the Knuth-Bendix +algorithm for semigroups and monoids in ``libsemigroups``. This page contains +details of the member functions of the class :any:`KnuthBendixRewriteTrie`. +This class is used to represent a `string rewriting system +`_ defining a 1- or 2-sided congruence on a finitely +presented monoid or semigroup. + +:any:`KnuthBendixRewriteTrie` inherits from :any:`Runner` and +:any:`CongruenceInterface`; and has the nested class +:any:`KnuthBendixRewriteTrie.options`. + + .. doctest:: + + >>> from libsemigroups_pybind11 import (KnuthBendix, Presentation, + ... presentation, congruence_kind) + >>> p = Presentation("abc") + >>> presentation.add_rule(p, "aaaa", "a") + >>> presentation.add_rule(p, "bbbb", "b") + >>> presentation.add_rule(p, "cccc", "c") + >>> presentation.add_rule(p, "abab", "aaa") + >>> presentation.add_rule(p, "bcbc", "bbb") + >>> kb = KnuthBendix(congruence_kind.twosided, p) + >>> not kb.confluent() + True + >>> kb.run() + >>> kb.number_of_active_rules() + 31 + >>> kb.confluent() + True +)pbdoc"); + + py::class_::options> options(kb, + "options", + R"pbdoc( +This class containing various options that can be used to control the +behaviour of Knuth-Bendix.)pbdoc"); + + py::enum_::options::overlap>(options, + "overlap", + R"pbdoc( +Values for specifying how to measure the length of an overlap. + +The values in this enum determine how a :any:`KnuthBendixRewriteTrie` +instance measures the length :math:`d(AB, BC)` of the overlap of +two words :math:`AB` and :math:`BC`. + +.. seealso:: :any:`KnuthBendixRewriteTrie.overlap_policy` +)pbdoc") + .value("ABC", + KnuthBendix::options::overlap::ABC, + R"pbdoc(:math:`d(AB, BC) = |A| + |B| + |C|`)pbdoc") + .value("AB_BC", + KnuthBendix::options::overlap::AB_BC, + R"pbdoc(:math:`d(AB, BC) = |AB| + |BC|`)pbdoc") + .value("MAX_AB_BC", + KnuthBendix::options::overlap::MAX_AB_BC, + R"pbdoc(:math:`d(AB, BC) = max(|AB|, |BC|)`)pbdoc"); + + kb.def("__repr__", [](KnuthBendix& kb) { + return to_human_readable_repr(kb); + }); + + ////////////////////////////////////////////////////////////////////////// + // Initialisers + ////////////////////////////////////////////////////////////////////////// + + kb.def(py::init<>(), + R"pbdoc( +Constructs a :any:`KnuthBendixRewriteTrie` instance with no +rules, and the short-lex reduction ordering. + +This function default constructs an uninitialised :any:`KnuthBendixRewriteTrie` +instance. +)pbdoc") + .def(py::init const&>(), + py::arg("knd"), + py::arg("p"), + // TODO(0) this constructor's doc is duplicated, but including + // :only-document-once: suppresses the default constructors doc. + R"pbdoc( +:sig=(knd: congruence_kind, p: PresentationStrings) -> None: + +Construct from :any:`congruence_kind` and :any:`PresentationStrings`. + +This function constructs a :any:`KnuthBendixRewriteTrie` instance representing +a congruence of kind *knd* over the semigroup or monoid defined by the +presentation *p*. + +:param knd: the kind (onesided or twosided) of the congruence. +:type knd: congruence_kind + +:param p: the presentation. +:type p: PresentationStrings + +:raises LibsemigroupsError: if *p* is not valid.)pbdoc") + .def(py::init const&>(), + R"pbdoc( +:sig=(knd: congruence_kind, p: PresentationStrings) -> None: +TODO(0) remove this +)pbdoc") + .def( + "init", + [](KnuthBendix& self) { return self.init(); }, + R"pbdoc( +Remove the presentation and rewriter data. This function clears the rewriter, +presentation, settings and stats from the :any:`KnuthBendixRewriteTrie` object, +putting it back into the state it would be in if it was newly default +constructed. + +:returns: ``self``. + +:rtype: + KnuthBendix +)pbdoc") + .def( + "init", + [](KnuthBendix& self, + congruence_kind knd, + Presentation const& p) { + return self.init(knd, p); + }, + py::arg("knd"), + py::arg("p"), + R"pbdoc( +:sig=(self: KnuthBendixRewriteTrie, knd: congruence_kind, p: Presentation) -> KnuthBendixRewriteTrie: +:only-document-once: + +Re-initialize a KnuthBendix instance. + +This function puts a :any:`KnuthBendixRewriteTrie` instance back into the state +that it would have been in if it had just been newly constructed from *knd* and +*p*. + +:param knd: the kind (onesided or twosided) of the congruence. +:type knd: congruence_kind + +:param p: the presentation. +:type p: Presentation + +:returns: ``self``. +:rtype: KnuthBendix + +:raises LibsemigroupsError: if *p* is not valid. +)pbdoc") + .def( + "init", + [](KnuthBendix& self, + congruence_kind knd, + Presentation const& p) { + return self.init(knd, p); + }, + py::arg("knd"), + py::arg("p"), + R"pbdoc( +:sig=(self: KnuthBendixRewriteTrie, knd: congruence_kind, p: Presentation) -> KnuthBendixRewriteTrie: +:only-document-once: +)pbdoc") + .def( + "copy", + [](KnuthBendix const& self) { + return KnuthBendix(self); + }, + R"pbdoc( +Copy a :py:class:`KnuthBendixRewriteTrie` object. + +:returns: A copy. +:rtype: KnuthBendixRewriteTrie +)pbdoc") + .def("__copy__", [](KnuthBendix const& self) { + return KnuthBendix(self); + }); + + ////////////////////////////////////////////////////////////////////////// + // Setters and getters for optional parameters + ////////////////////////////////////////////////////////////////////////// + + kb.def("max_pending_rules", + py::overload_cast<>(&KnuthBendix::max_pending_rules, + py::const_), + R"pbdoc( +Return the number of pending rules that must accumulate before they are reduced, +processed, and added to the system. + +The default value is ``128``. A value of ``1`` means :any:`Runner.run` should +attempt to add each rule as they are created without waiting for rules to +accumulate. + +:return: The batch size. +:rtype: int + +.. seealso:: :any:`Runner.run`. +)pbdoc") + .def("max_pending_rules", + py::overload_cast( + &KnuthBendix::max_pending_rules), + py::arg("val"), + R"pbdoc( +Specify the number of pending rules that must accumulate before they are +reduced, processed, and added to the system. + +The default value is ``128``, and should be set to ``1`` if :any:`Runner.run` +should attempt to add each rule as they are created without waiting for rules +to accumulate. + +:param val: The new value of the batch size. +:type val: int + +:return: ``self``. +:rtype: KnuthBendixRewriteTrie + +.. seealso:: :any:`Runner.run`. +)pbdoc") + .def("check_confluence_interval", + py::overload_cast<>( + &KnuthBendix::check_confluence_interval, + py::const_), + R"pbdoc( +Return the interval at which confluence is checked. + +The function :any:`Runner.run` periodically checks if the system is already +confluent. This function can be used to return how frequently this happens. It +is the number of new overlaps that should be considered before checking +confluence. + +:return: The interval at which confluence is checked. +:rtype: int + +.. seealso:: :any:`Runner.run`. +)pbdoc") + .def("check_confluence_interval", + py::overload_cast(&libsemigroups::KnuthBendix< + Rewriter>::check_confluence_interval), + py::arg("val"), + R"pbdoc( +Set the interval at which confluence is checked. + +The function :py:meth:`Runner.run` periodically checks if the system is already +confluent. This function can be used to set how frequently this happens. It is +the number of new overlaps that should be considered before checking confluence. +Setting this value too low can adversely affect the performance of +:any:`Runner.run`. + +The default value is ``4096``, and should be set to +:py:obj:`LIMIT_MAX` if :any:`Runner.run` should never check if the +system is already confluent. + +:param val: The new value of the interval. +:type val: int + +:return: ``self``. +:rtype: KnuthBendixRewriteTrie +)pbdoc") + .def("max_overlap", + py::overload_cast<>(&KnuthBendix::max_overlap, + py::const_), + R"pbdoc( +Return the maximum length of overlaps to be considered. + +This function returns the maximum length of the overlap of two left hand sides +of rules that should be considered in :any:`Runner.run`. + +:return: The maximum overlap length +:rtype: int + +.. seealso:: :any:`Runner.run`. +)pbdoc") + .def("max_overlap", + py::overload_cast( + &libsemigroups::KnuthBendix::max_overlap), + py::arg("val"), + R"pbdoc( +Set the maximum length of overlaps to be considered. + +This function can be used to specify the maximum length of the overlap of two +left hand sides of rules that should be considered in :any:`Runner.run`. + +If this value is less than the longest left hand side of a rule, then +:any:`Runner.run` can terminate without the system being confluent. + +:param val: The new value of the maximum overlap length. +:type val: int + +:return: ``self``. +:rtype: KnuthBendixRewriteTrie + +.. seealso:: :any:`Runner.run`. +)pbdoc") + .def("max_rules", + py::overload_cast<>(&KnuthBendix::max_rules, + py::const_), + R"pbdoc( +Return the maximum number of rules. + +This member function returns the (approximate) maximum number of rules that the +system should contain. If this is number is exceeded in calls to :any:`Runner.run` +or :any:`knuth_bendix.by_overlap_length`, then they will terminate and the +system may not be confluent. + +:return: The maximum number of rules the system should contain. +:rtype: int + +.. seealso:: :any:`Runner.run`. +)pbdoc") + .def("max_rules", + py::overload_cast( + &libsemigroups::KnuthBendix::max_rules), + py::arg("val"), + R"pbdoc( +Set the maximum number of rules. + +This member function sets the (approximate) maximum number of rules that the +system should contain. If this is number is exceeded in calls to :any:`Runner.run` +or :any:`knuth_bendix.by_overlap_length`, then they will terminate and the +system may not be confluent. + +By default this value is :any:`POSITIVE_INFINITY`. + +:param val: The maximum number of rules. +:type val: int + +:return: ``self``. +:rtype: KnuthBendixRewriteTrie + +.. seealso:: :any:`Runner.run`. +)pbdoc") + .def("overlap_policy", + py::overload_cast<>(&KnuthBendix::overlap_policy, + py::const_), + R"pbdoc( +:sig=(self: KnuthBendixRewriteTrie) -> KnuthBendixRewriteTrie.options.overlap: + +Return the overlap policy. + +This function returns the way that the length of an overlap of two words in the +system is measured. + +:return: The overlap policy. +:rtype: overlap + +.. seealso:: :py:class:`overlap`. +)pbdoc") + .def("overlap_policy", + py::overload_cast< + typename KnuthBendix::options::overlap>( + &KnuthBendix::overlap_policy), + py::arg("val"), + R"pbdoc( +:sig=(self: KnuthBendixRewriteTrie, val: KnuthBendixRewriteTrie.options.overlap) -> KnuthBendixRewriteTrie: + +Set the overlap policy. + +This function can be used to determine the way that the length of an overlap of +two words in the system is measured. + +:param val: The overlap policy. +:type val: overlap + +:return: ``self``. +:rtype: KnuthBendixRewriteTrie + +.. seealso:: :py:class:`overlap` +)pbdoc"); + ////////////////////////////////////////////////////////////////////////// + // Member functions for rules and rewriting + ////////////////////////////////////////////////////////////////////////// + + kb.def( + "presentation", + [](KnuthBendix& kb) { return kb.presentation(); }, + R"pbdoc( +Return the presentation defined by the rewriting system + +:return: The presentation +:rtype: PresentationStrings +)pbdoc") + .def("number_of_active_rules", + &libsemigroups::KnuthBendix::number_of_active_rules, + R"pbdoc( +Return the current number of active rules. + +:return: The current number of active rules. +:rtype: int +)pbdoc") + .def("number_of_inactive_rules", + &libsemigroups::KnuthBendix::number_of_inactive_rules, + R"pbdoc( +Return the current number of inactive rules. + +:return: The current number of inactive rules. +:rtype: int +)pbdoc") + .def("total_rules", + &libsemigroups::KnuthBendix::total_rules, + R"pbdoc( +Return the number of rules that have been created + +Return the total number of Rule instances that have been created whilst whilst +the Knuth-Bendix algorithm has been running. Note that this is not the sum of +:any:`number_of_active_rules` and :any:`number_of_inactive_rules`, due +to the re-initialisation of rules where possible. + +:return: The total number of rules. +:rtype: int +)pbdoc") + .def( + "active_rules", + [](KnuthBendix& kb) { + auto rules = kb.active_rules(); + return py::make_iterator(rx::begin(rules), rx::end(rules)); + }, + R"pbdoc( +Return a copy of the active rules. + +This member function returns an iterator yielding of the pairs of strings +which represent the rewriting rules. The first entry in every such pair is +greater than the second according to the reduction ordering of the +:py:class:`KnuthBendixRewriteTrie` instance. The rules are sorted +according to the reduction ordering used by the rewriting system, on the first +entry. + +:return: An iterator yielding the currently active rules. +:rtype: Iterator[(str, str)] +)pbdoc"); + + reduce_no_run(kb); + reduce_no_run(kb); + + reduce(kb); + reduce(kb); + + contains(kb); + contains(kb); + + currently_contains(kb); + currently_contains(kb); + + ////////////////////////////////////////////////////////////////////////// + // Main member + // functions + ////////////////////////////////////////////////////////////////////////// + + kb.def("confluent", + &libsemigroups::KnuthBendix::confluent, + R"pbdoc( +Check `confluence `_ of the current rules. + +:return: ``True`` if the :py:class:`KnuthBendixRewriteTrie` + instance is confluent and ``False`` if it is not. +:rtype: bool +)pbdoc") + .def("confluent_known", + &libsemigroups::KnuthBendix::confluent_known, + R"pbdoc( +Check if the current system knows the state of confluence of the current rules. + +:return: + ``True`` if the confluence of the rules in the + :py:class:`KnuthBendixRewriteTrie` instance is known, and + ``False`` if it is not. +:rtype: bool +)pbdoc") + .def( + "gilman_graph", + [](KnuthBendix& kb) -> WordGraph const& { + return kb.gilman_graph(); + }, + py::return_value_policy::copy, + // REVIEW: Should WordGraph be formatted as code, or as text? + R"pbdoc( +Return the Gilman :py:class:`WordGraph`. + +The Gilman :py:class:`WordGraph` is a digraph where the labels of the paths from +the initial node (corresponding to the empty word) correspond to the short-lex +normal forms of the semigroup elements. + +The semigroup is finite if the graph is cyclic, and infinite otherwise. + +:return: The Gilman :py:class:`WordGraph`. +:rtype: WordGraph + +.. warning:: This will terminate when the + :any:`KnuthBendixRewriteTrie` instance is reduced and + confluent, which might be never. + +.. seealso:: :any:`number_of_classes` and :any:`knuth_bendix.normal_forms`. +)pbdoc") + .def("gilman_graph_node_labels", + &libsemigroups::KnuthBendix::gilman_graph_node_labels, + R"pbdoc( +Return the node labels of the Gilman :py:class:`WordGraph` + +Return the node labels of the Gilman :py:class:`WordGraph`, corresponding to the +unique prefixes of the left-hand sides of the rules of the rewriting system. + +:return: The node labels of the Gilman :py:class:`WordGraph` +:rtype: List[str] + +.. seealso:: :any:`gilman_graph`. +)pbdoc"); + + ////////////////////////////////////////////////////////////////////////// + // Attributes + ////////////////////////////////////////////////////////////////////////// + + kb.def("_number_of_classes", + &libsemigroups::KnuthBendix::number_of_classes, + R"pbdoc( +:sig=(kb: KnuthBendixRewriteTrie) -> int | PositiveInfinity: + +Compute the number of classes in the congruence. + +This function computes the number of classes in the congruence represented by a +:py:class:`KnuthBendixRewriteTrie` instance. + +:returns: + The number of congruences classes of a :py:class:`KnuthBendixRewriteTrie` object. +:rtype: + int | PositiveInfinity +)pbdoc"); + + ////////////////////////////////////////////////////////////////////////// + // Helpers + ////////////////////////////////////////////////////////////////////////// + + m.def( + "knuth_bendix_by_overlap_length", + [](KnuthBendix& kb) { + knuth_bendix::by_overlap_length(kb); + }, + py::arg("kb"), + R"pbdoc( +:sig=(kb: KnuthBendixRewriteTrie) -> None: +:only-document-once: + +Run the Knuth-Bendix algorithm by considering all overlaps of a given length. + +This function runs the Knuth-Bendix algorithm on the rewriting system +represented by a :any:`KnuthBendixRewriteTrie` instance by considering all overlaps of a +given length :math:`n` (according to the :any:`libsemigroups_pybind11.overlap`) before those overlaps +of length :math:`n + 1`. + +:param kb: the :any:`KnuthBendixRewriteTrie` instance. +:type kb: KnuthBendixRewriteTrie +)pbdoc"); + m.def( + "knuth_bendix_is_reduced", + [](KnuthBendix& kb) { + return knuth_bendix::is_reduced(kb); + }, + py::arg("kb"), + R"pbdoc( +:sig=(kb: KnuthBendixRewriteTrie) -> bool: +:only-document-once: + +Check if the all rules are reduced with respect to each other. + +:param kb: the KnuthBendix instance defining the rules that are to be checked for being reduced. +:type kb: KnuthBendixRewriteTrie + +:returns: + ``True`` if for each pair :math:`(A, B)` and :math:`(C, D)` of rules stored + within the :any:`KnuthBendixRewriteTrie` instance, :math:`C` is neither a subword of + :math:`A` nor :math:`B`. Returns ``False`` otherwise. +:rtype: bool +)pbdoc"); + + // The next 2 functions are documented in the wrapper in + // libsemigroups_pybind11/knuth_bendix.py, because they have the + // additional kwarg Word to specify the output type. + m.def("knuth_bendix_str_non_trivial_classes", + [](KnuthBendix& kb1, KnuthBendix& kb2) { + return knuth_bendix::non_trivial_classes(kb1, kb2); + }); + m.def("knuth_bendix_word_non_trivial_classes", + [](KnuthBendix& kb1, KnuthBendix& kb2) { + return knuth_bendix::non_trivial_classes(kb1, kb2); + }); + + bind_normal_form_range( + m, (name + "_strings").c_str()); + bind_normal_form_range(m, (name + "_words").c_str()); + + // The next 2 functions are documented in the wrapper in + // libsemigroups_pybind11/knuth_bendix.py, because they have the + // additional kwarg Word to specify the output type. + m.def("knuth_bendix_str_normal_forms", [](KnuthBendix& kb) { + return knuth_bendix::normal_forms(kb); + }); + m.def("knuth_bendix_word_normal_forms", [](KnuthBendix& kb) { + return knuth_bendix::normal_forms(kb); + }); + + 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: PresentationStrings, t: datetime.timedelta) -> Tuple[List[int], List[int]] | Tuple[str, str] | None: +:only-document-once: + +Return a redundant rule or ``None``. + +Starting with the last rule in the presentation, this function attempts to run +the Knuth-Bendix algorithm on the rules of the presentation except for a given +omitted rule. For every such omitted rule, Knuth-Bendix is run for the length +of time indicated by the second parameter *t*, and then it is checked if the +omitted rule can be shown to be redundant. If the omitted rule can be shown to +be redundant in this way, then this rule is returned If no rule can be shown to +be redundant in this way, then ``None`` is returned. + +:param p: the presentation. +:type p: PresentationStrings + +:param t: time to run Knuth-Bendix for every omitted rule. +:type t: datetime.timedelta + +:returns: A redundant rule or ``None``. +:rtype: Tuple[List[int], List[int]] | Tuple[str, str] | None + +:warning: + The progress of the Knuth-Bendix algorithm may differ between different + calls to this function even if the parameters are identical. As such this + is non-deterministic, and may produce different results with the same + input. + +.. doctest:: + + >>> from libsemigroups_pybind11 import knuth_bendix, presentation, Presentation + >>> from datetime import timedelta + >>> p = Presentation("ab") + >>> presentation.add_rule(p, "ab", "ba") + >>> presentation.add_rule(p, "bab", "abb") + >>> t = timedelta(seconds = 1) + >>> p.rules + ['ab', 'ba', 'bab', 'abb'] + >>> knuth_bendix.redundant_rule(p, t) + ('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: PresentationStrings, t: datetime.timedelta) -> Tuple[List[int], List[int]] | Tuple[str, str] | None: +:only-document-once: +)pbdoc"); + m.def( + "is_obviously_infinite", + [](KnuthBendix& kb) { return is_obviously_infinite(kb); }, + R"pbdoc( +:sig=(kb: KnuthBendixRewriteTrie) -> bool: +:only-document-once: + +Function for checking if the quotient of a finitely presented semigroup or +monoid defined by a :py:class:`KnuthBendixRewriteTrie` object is obviously +infinite or not. + +This function returns ``True`` if the quotient of the finitely presented +semigroup or monoid defined by the :py:class:`KnuthBendixRewriteTrie` object +*kb* is obviously infinite; ``False`` is returned if it is not. + +:param kb: the :py:class:`KnuthBendixRewriteTrie` instance. +:type kb: KnuthBendix + +:returns: + Whether or not the congruence defined by a + :py:class:`KnuthBendixRewriteTrie` instance obviously has infinitely many + classes. +:rtype: + bool + +.. note:: + If this function returns ``False``, it is still possible that the quotient + defined by the :py:class:`KnuthBendixRewriteTrie` object *kb* is infinite. +)pbdoc"); + } + } // namespace + + void init_knuth_bendix(py::module& m) { + bind_knuth_bendix(m, "KnuthBendixRewriteFromLeft"); + bind_knuth_bendix(m, "KnuthBendixRewriteTrie"); + } +} // namespace libsemigroups diff --git a/src/main.cpp b/src/main.cpp index c7d2dd00..4c7e06ed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,7 +210,6 @@ The valid values are: init_forest(m); init_freeband(m); init_gabow(m); - // init_knuth_bendix(m); init_order(m); init_obvinf(m); init_paths(m); @@ -230,6 +229,7 @@ The valid values are: init_cong_intf(m); init_todd_coxeter(m); init_kambites(m); + init_knuth_bendix(m); #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; @@ -248,34 +248,6 @@ The valid values are: .value("onesided", congruence_kind::onesided) .value("twosided", congruence_kind::twosided); - // TODO better repr? - py::enum_::options::overlap>(m, - "overlap", - R"pbdoc( - Values for specifying how to measure the length of an overlap. - - The values in this enum determine how a :py:class:`_libsemigroups_pybind11.KnuthBendixRewriteTrie` - instance measures the length :math:`d(AB, BC)` of the overlap of - two words :math:`AB` and :math:`BC`. - - .. seealso:: :py:meth:`KnuthBendix.overlap_policy()<_libsemigroups_pybind11.KnuthBendixRewriteTrie.overlap_policy>` - )pbdoc") - .value("ABC", - KnuthBendix<>::options::overlap::ABC, - R"pbdoc( - :math:`d(AB, BC) = |A| + |B| + |C|` - )pbdoc") - .value("AB_BC", - KnuthBendix<>::options::overlap::AB_BC, - R"pbdoc( - :math:`d(AB, BC) = |AB| + |BC|` - )pbdoc") - .value("MAX_AB_BC", - KnuthBendix<>::options::overlap::MAX_AB_BC, - R"pbdoc( - :math:`d(AB, BC) = max(|AB|, |BC|)` - )pbdoc"); - py::class_(m, "ReportGuard", R"pbdoc( diff --git a/src/old/fpsemi.cpp b/src/old/fpsemi.cpp deleted file mode 100644 index 342735a8..00000000 --- a/src/old/fpsemi.cpp +++ /dev/null @@ -1,593 +0,0 @@ -// -// libsemigroups - C++ library for semigroups and monoids -// Copyright (C) 2021-2024 James D. Mitchell -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -// C std headers.... -#include // for size_t - -// C++ stl headers.... -#include // for array -#include // for nanoseconds -#include // for function -#include // for initializer_list -#include // for string -#include // for shared_ptr -#include // for vector - -// libsemigroups.... - -#include // for Runner -#include // for word_type, letter_type, relation... - -// pybind11.... -#include -#include -#include // for class_, init, make_iterator, module -#include - -// libsemigroups_pybind11.... -#include "main.hpp" // for init_fpsemi - -namespace libsemigroups { /* - class FroidurePinBase; - */ -} - -namespace py = pybind11; - -namespace libsemigroups { - /* - using rule_type = FpSemigroupInterface::rule_type; - void init_fpsemi(py::module& m) { - py::class_(m, "FpSemigroup") - .def(py::init<>()) - .def(py::init>()) - .def(py::init()) - .def("validate_letter", - py::overload_cast(&FpSemigroup::validate_letter, py::const_), - py::arg("c"), - R"pbdoc( - Validates a letter. - - :Parameters: **c** (str) - the letter to validate. - - :Returns: (None) - )pbdoc") - .def("validate_letter", - py::overload_cast(&FpSemigroup::validate_letter, - py::const_), - py::arg("c"), - R"pbdoc( - Validates a letter. - - :Parameters: **c** (int) - the letter to validate. - - :Returns: (None) - )pbdoc") - .def("validate_word", - py::overload_cast(&FpSemigroup::validate_word, - py::const_), - py::arg("w"), - R"pbdoc( - Validates a word. - - :Parameters: **w** (List[int]) - the word to validate. - - :Returns: (None) - )pbdoc") - .def("validate_word", - py::overload_cast(&FpSemigroup::validate_word, - py::const_), - py::arg("w"), - R"pbdoc( - Validates a word. - - :Parameters: **w** (str) - the word to validate. - - :Returns: (None) - )pbdoc") - .def("set_alphabet", - py::overload_cast(&FpSemigroup::set_alphabet), - py::arg("n"), - R"pbdoc( - Set the size of the alphabet. - - :Parameters: **n** (int) - the number of letters. - - :Returns: (None) - )pbdoc") - .def("set_alphabet", - py::overload_cast(&FpSemigroup::set_alphabet), - py::arg("a"), - R"pbdoc( - Set the alphabet of the finitely presented semigroup. - - :Parameters: **a** (str) - the alphabet. - - :Returns: (None) - )pbdoc") - .def("alphabet", - py::overload_cast<>(&FpSemigroup::alphabet, py::const_), - R"pbdoc( - Returns the alphabet of the finitely presented semigroup - represented. - - - :Returns: A string. - )pbdoc") - .def("alphabet", - py::overload_cast(&FpSemigroup::alphabet, py::const_), - py::arg("i"), - R"pbdoc( - Returns the i-th letter of the alphabet of the finitely - presented semigroup. - - :Parameters: **i** (int) - the index of the letter. - - :Returns: A string. - )pbdoc") - .def("set_identity", - py::overload_cast(&FpSemigroup::set_identity), - py::arg("id"), - R"pbdoc( - Set a string of length 1 belonging to - :py:meth:`~FpSemigroup.alphabet` to be the identity using its - index. - - :Parameters: **id** (int) - the index of the character to be the - identity. - - :Returns: (None) - )pbdoc") - .def("set_identity", - py::overload_cast(&FpSemigroup::set_identity), - py::arg("id"), - R"pbdoc( - Set a string of length 1 belonging to - :py:meth:`~FpSemigroup.alphabet` to be the identity. - - :Parameters: **id** (str) - a string containing the character to - be the identity. - - :Returns: (None) - )pbdoc") - .def("identity", - &FpSemigroup::identity, - R"pbdoc( - Returns the identity of this, or raises an exception if there - isn't one. - - :return: A string. - )pbdoc") - .def("set_inverses", - &FpSemigroup::set_inverses, - py::arg("a"), - R"pbdoc( - Set the inverses of letters in :py:meth:`~FpSemigroup.alphabet`. - - :param a: a string of length ``alphabet().size()``. - :type a: str - - :return: (None) - )pbdoc") - .def("inverses", - &FpSemigroup::inverses, - R"pbdoc( - Returns the inverses of this, or raises an exception if there - aren't any. - - :return: A string. - )pbdoc") - .def("add_rule", - py::overload_cast(&FpSemigroup::add_rule), - py::arg("rel"), - R"pbdoc( - Add a rule. - - :Parameters: **rel** (Tuple[List[int], List[int]]) - the rule - being added. - - :Returns: (None) - )pbdoc") - .def("add_rule", - py::overload_cast(&FpSemigroup::add_rule), - py::arg("rel"), - R"pbdoc( - Add a rule. - - :Parameters: **rel** (Tuple[str, str]) - the rule being added. - - :Returns: (None) - )pbdoc") - .def("add_rule", - py::overload_cast( - &FpSemigroup::add_rule), - py::arg("u"), - py::arg("v"), - R"pbdoc( - Add a rule. - - :Parameters: - **u** (str) - the left-hand side of the rule being - added. - - **v** (str) - the right-hand side of the rule - being added. - - :Returns: (None) - )pbdoc") - .def("add_rule", - py::overload_cast( - &FpSemigroup::add_rule), - py::arg("u"), - py::arg("v"), - R"pbdoc( - Add a rule. - - :Parameters: - **u** (List[int]) - the left-hand side of the rule - being added. - - **v** (List[int]) - the right-hand side of the - rule being added. - - :Returns: (None) - )pbdoc") - .def("add_rules", - py::overload_cast(&FpSemigroup::add_rules), - py::arg("S"), - R"pbdoc( - Add the rules of a finite presentation for S to this. - - :Parameters: **S** (:py:class:`FroidurePin`) - a FroidurePin - object representing a semigroup. - - :Returns: (None) - )pbdoc") - .def("add_rules", - py::overload_cast const&>( - &FpSemigroup::add_rules), - py::arg("rels"), - R"pbdoc( - Add the rules in the given list to the finitely presented - semigroup. - - :Parameters: **rels** (List[Tuple[str, str]]) - the rules to add. - - :Returns: (None) - )pbdoc") - .def( - "number_of_rules", - [](FpSemigroup const& fp) { return fp.number_of_rules(); }, - R"pbdoc( - Returns the number of rules currently used to define the - finitely presented semigroups. - - :return: An int. - )pbdoc") - .def("report_every", - (void (FpSemigroup::*)(std::chrono::nanoseconds)) - & Runner::report_every, - py::arg("t"), - R"pbdoc( - Set the minimum elapsed time between reports. - - :Parameters: **t** (datatime.timedelta) - the amount of time - between reports. - - :Returns: (None) - )pbdoc") - .def("report", - &FpSemigroup::report, - R"pbdoc( - Check if it is time to report. - - :return: A bool. - )pbdoc") - .def("report_why_we_stopped", - &FpSemigroup::report_why_we_stopped, - R"pbdoc( - Report why the algorithm stopped. - - :return: (None) - )pbdoc") - .def("kill", - &FpSemigroup::kill, - R"pbdoc( - Stop the algorithm from running (thread-safe). - - :return: (None). - )pbdoc") - .def("run", - &FpSemigroup::run, - R"pbdoc( - Run the algorithm. - - :return: (None) - )pbdoc") - .def("run_for", - (void (FpSemigroup::*)(std::chrono::nanoseconds)) - & Runner::run_for, - py::arg("t"), - R"pbdoc( - Run for a specified amount of time. - - :Parameters: **t** (datetime.timedelta) - the time to run for. - - :Returns: (None) - )pbdoc") - .def("run_until", - (void (FpSemigroup::*)(std::function&)) - & Runner::run_until, - py::arg("func"), - R"pbdoc( - Run until a nullary predicate returns ``True`` or the algorithm - is finished. - - :Parameters: **func** (Callable[], bool) - the nullary predicate. - - :Returns: (None) - )pbdoc") - .def("dead", - &FpSemigroup::dead, - R"pbdoc( - Check if the algorithm was killed. - - :return: A ``bool``. - )pbdoc") - .def("finished", - &FpSemigroup::finished, - R"pbdoc( - Check if the algorithm has been run to completion or not. - - :return: A ``bool``. - )pbdoc") - .def("started", - &FpSemigroup::started, - R"pbdoc( - Check if the algorithm has started. - - :return: A ``bool``. - )pbdoc") - .def("stopped", - &FpSemigroup::stopped, - R"pbdoc( - Check if the algorithm is stopped. - - :return: A ``bool``. - )pbdoc") - .def("timed_out", - &FpSemigroup::timed_out, - R"pbdoc( - Check if the amount of time specified to - :py:meth:`~FpSemigroup.run_for` has elapsed. - - :return: A ``bool``. - )pbdoc") - .def( - "running", - [](FpSemigroup const& fp) { return fp.running(); }, - R"pbdoc( - Check if the algorithm is currently running. - - :return: ``True`` if algorithm is in the process of running and - ``False`` it is not. )pbdoc") .def("stopped_by_predicate", - &FpSemigroup::stopped_by_predicate, - R"pbdoc( - Check if the runner was, or should, stop because the nullary - predicate passed as first argument to - :py:meth:`~FpSemigroup.run_until` return ``True``. - - :return: A ``bool``. - )pbdoc") - .def("normal_form", - py::overload_cast(&FpSemigroup::normal_form), - py::arg("w"), - R"pbdoc( - Returns a normal form for a string. - - :Parameters: **w** (str) - the word whose normal form we want to - find. The parameter w must consist of letters in - :py:meth:`~FpSemigroup.alphabet()`. - - :Returns: A string. - )pbdoc") - .def("normal_form", - py::overload_cast(&FpSemigroup::normal_form), - py::arg("w"), - R"pbdoc( - Returns a normal form for a list of integers. - - :Parameters: **w** (List[int]) - the word whose normal form we - want to find. The parameter ``w`` consist of indices of the generators of the - finitely presented semigroup that ``self`` represents. - - :Returns: A list of integers. - )pbdoc") - .def("equal_to", - py::overload_cast( - &FpSemigroup::equal_to), - py::arg("u"), - py::arg("v"), - R"pbdoc( - Check if two words represent the same element. - - :Parameters: - **u** (str) - the first word, must be a string - over :py:meth:`~FpSemigroup.alphabet`. - - **v** (str) - the second word, must be a string - over :py:meth:`~FpSemigroup.alphabet`. - - :Returns: ``True`` if the strings ``u`` and ``v`` represent the - same element of the finitely presented semigroup, and ``False`` otherwise. - )pbdoc") - .def("equal_to", - py::overload_cast( - &FpSemigroup::equal_to), - py::arg("u"), - py::arg("v"), - R"pbdoc( - Check if two words represent the same element. - - :Parameters: - **u** (List[int]) - the first word. - - **v** (List[int]) - the second word. - - :Returns: ``True`` if the words ``u`` and ``v`` represent the - same element of the finitely presented semigroup, and ``False`` otherwise. - )pbdoc") - .def("word_to_string", - &FpSemigroup::word_to_string, - py::arg("w"), - R"pbdoc( - Convert a list of integers to a string representing the same - element of the finitely presented semigroup. - - :param w: the word to convert. - :type w: List[int] - - :return: A string. - )pbdoc") - .def("string_to_word", - &FpSemigroup::string_to_word, - py::arg("w"), - R"pbdoc( - Convert a string to a list of integers representing the - same element of the finitely presented semigroup. - - :param w: the string to convert. - :type w: str - - :return: A list of integers. - )pbdoc") - .def("uint_to_char", - &FpSemigroup::uint_to_char, - py::arg("a"), - R"pbdoc( - Convert an ``int`` to a string of length 1 representing the same - generator of the finitely presented semigroup. - - :param a: the int to convert. - :type a: int - - :return: A string of length 1. - )pbdoc") - .def("char_to_uint", - &FpSemigroup::char_to_uint, - py::arg("a"), - R"pbdoc( - Convert a string of length 1 to an ``int`` representing the same - generator of the finitely presented semigroup. - - :param a: the string to convert. - :type a: str - - :return: An ``int``. - )pbdoc") - .def( - "has_froidure_pin", - [](FpSemigroup const& x) { return x.has_froidure_pin(); }, - R"pbdoc( - Returns True if a ``FroidurePin`` instance isomorphic to the - finitely presented semigroup has already been - computed, and False if not. - - :return: A ``bool``. - )pbdoc") - .def( - "froidure_pin", - [](FpSemigroup& x) { return x.froidure_pin(); }, - R"pbdoc( - Returns a ``FroidurePin`` instance isomorphic to the finitely - presented semigroup. - - :return: A ``FroidurePin`` instance. - )pbdoc") - .def("has_knuth_bendix", - &FpSemigroup::has_knuth_bendix, - R"pbdoc( - Checks if a ``KnuthBendix`` instance is being used to - compute the finitely presented semigroup. - - :return: A ``bool``. - )pbdoc") - .def("knuth_bendix", - &FpSemigroup::knuth_bendix, - R"pbdoc( - Returns the ``KnuthBendix`` instance used to compute - the finitely presented semigroup (if any). - - :return: A ``KnuthBendix`` instance. - )pbdoc") - .def("has_todd_coxeter", - &FpSemigroup::has_todd_coxeter, - R"pbdoc( - Checks if a ``ToddCoxeter`` instance is being used to - compute the finitely presented semigroup. - - :return: A ``bool``. - )pbdoc") - .def("todd_coxeter", - &FpSemigroup::todd_coxeter, - R"pbdoc( - Returns the ``ToddCoxeter`` instance - used to compute the finitely presented semigroup (if any). - - :return: A ``ToddCoxeter`` instance. - )pbdoc") - .def("is_obviously_finite", - &FpSemigroup::is_obviously_finite, - R"pbdoc( - Return ``True`` if the finitely presented semigroup is obviously - finite, and ``False`` if it is not obviously finite. - - :return: A ``bool``. - )pbdoc") - .def("is_obviously_infinite", - &FpSemigroup::is_obviously_infinite, - R"pbdoc( - Return ``True`` if the finitely presented semigroup is obviously - infinite, and ``False`` if it is not obviously infinite. - - :return: A ``bool``. - )pbdoc") - .def("size", - &FpSemigroup::size, - R"pbdoc( - Returns the size of the finitely presented semigroup. - - :return: - An ``int`` the value of which equals the size of this - if this number is finite, or ``POSITIVE_INFINITY`` if this - number is not finite. - )pbdoc") - .def( - "rules", - [](FpSemigroup const& fp) { - return py::make_iterator(fp.cbegin_rules(), fp.cend_rules()); - }, - R"pbdoc( - Returns an iterator to the rules used to define the finitely - presented semigroup. - - :return: An iterator. - )pbdoc") - .def("to_gap_string", - &FpSemigroup::to_gap_string, - R"pbdoc( - Returns a string containing GAP commands for defining a finitely - presented semigroup equal to that represented by ``self``. - - :return: A string. - )pbdoc"); - } -*/ -} // namespace libsemigroups diff --git a/src/old/knuth-bendix.cpp b/src/old/knuth-bendix.cpp deleted file mode 100644 index 943a7ea0..00000000 --- a/src/old/knuth-bendix.cpp +++ /dev/null @@ -1,588 +0,0 @@ -// libsemigroups - C++ library for semigroups and monoids -// Copyright (C) 2021-2024 James D. Mitchell, Maria Tsalakou -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -// C std headers.... -#include // for size_t - -// C++ stl headers.... -#include // for array -#include // for nanoseconds -#include // for __base, function -#include // for initializer_list -#include // for string -#include // for shared_ptr -#include // for vector - -// libsemigroups.... - -#include // for KnuthBendix, KnuthBendix::option... -#include // for WordGraph -#include // for Runner -#include // for word_type, letter_type -#include // for WordGraph - -// pybind11.... -#include -#include -#include // for class_, make_iterator, enum_, init -#include // for py::str -#include - -#include // for PyUnicode_DecodeLatin1 - -// libsemigroups_pybind11.... -#include "doc-strings.hpp" // for init_knuth_bendix -#include "main.hpp" // for init_knuth_bendix - -namespace py = pybind11; - -namespace libsemigroups { - - namespace { - - template - void bind_knuth_bendix(py::module& m, std::string const& name) { - py::class_, CongruenceInterface> kb(m, - name.c_str()); - kb.def("__repr__", - [](KnuthBendix& kb) { return knuth_bendix::repr(kb); }); - ////////////////////////////////////////////////////////////////////////// - // Initialisers - ////////////////////////////////////////////////////////////////////////// - kb.def(py::init const&>()) - .def(py::init()) - .def(py::init const&>()) - .def(py::init const&>()); - ////////////////////////////////////////////////////////////////////////// - // Setters and getters for optional parameters - ////////////////////////////////////////////////////////////////////////// - kb.def( - "batch_size", - py::overload_cast<>(&KnuthBendix::batch_size, py::const_), - R"pbdoc( -Return the number of pending rules that must accumulate before they are reduced, -processed, and added to the system. - -The default value is ``128``. A value of ``1`` means :py:meth:`run` should -attempt to add each rule as they are created without waiting for rules to -accumulate. - -:Parameters: None -:return: The batch size. -:rtype: int - -.. seealso:: :py:meth:`run`. - )pbdoc") - .def("batch_size", - py::overload_cast(&KnuthBendix::batch_size), - py::arg("val"), - R"pbdoc( -Specify the number of pending rules that must accumulate before they are -reduced, processed, and added to the system. - -The default value is ``128``, and should be set to ``1`` if :py:meth:`run` -should attempt to add each rule as they are created without waiting for rules -to accumulate. - -:param val: The new value of the batch size. -:type val: int -:return: A reference to ``self``. - -.. seealso:: :py:meth:`run`. - )pbdoc") - .def("check_confluence_interval", - py::overload_cast<>( - &KnuthBendix::check_confluence_interval, - py::const_), - R"pbdoc( -Return the interval at which confluence is checked. - -The function :py:meth:`run` periodically checks if the system is already -confluent. This function can be used to return how frequently this happens. It -is the number of new overlaps that should be considered before checking -confluence. - -:Parameters: None -:return: The interval at which confluence is checked. -:rtype: int - -.. seealso:: :py:meth:`run`. - )pbdoc") - .def("check_confluence_interval", - py::overload_cast(&libsemigroups::KnuthBendix< - Rewriter>::check_confluence_interval), - py::arg("val"), - R"pbdoc( -Set the interval at which confluence is checked. - -The function :py:meth`run` periodically checks if the system is already -confluent. This function can be used to set how frequently this happens. It is -the number of new overlaps that should be considered before checking confluence. -Setting this value too low can adversely affect the performance of -:py:meth:`run`. - -The default value is ``4096``, and should be set to -:py:obj:`LIMIT_MAX` if :py:meth:`run` should never check if the -system is already confluent. - -:param val: The new value of the interval. -:type val: int -:return: A reference to ``self``. - )pbdoc") - .def("max_overlap", - py::overload_cast<>(&KnuthBendix::max_overlap, - py::const_), - R"pbdoc( -Return the maximum length of overlaps to be considered. - -This function returns the maximum length of the overlap of two left hand sides -of rules that should be considered in :py:meth:`run`. - -:Parameters: None -:return: The maximum overlap length -:rtype: int - -.. seealso:: :py:meth:`run`. - )pbdoc") - .def("max_overlap", - py::overload_cast( - &libsemigroups::KnuthBendix::max_overlap), - py::arg("val"), - R"pbdoc( -Set the maximum length of overlaps to be considered. - -This function can be used to specify the maximum length of the overlap of two -left hand sides of rules that should be considered in :py:meth:`run`. - -If this value is less than the longest left hand side of a rule, then -:py:meth:`run` can terminate without the system being confluent. - -:param val: The new value of the maximum overlap length. -:type val: int -:return: A reference to ``self``. - -..seealso:: :py:meth:`run`. - )pbdoc") - .def("max_rules", - py::overload_cast<>(&KnuthBendix::max_rules, - py::const_), - R"pbdoc( -Return the maximum number of rules. - -This member function returns the (approximate) maximum number of rules that the -system should contain. If this is number is exceeded in calls to :py:meth:`run` -or -py:meth:`knuth_bendix.by_overlap_length`, -then they will terminate and the system may not be confluent. - -:Parameters: None -:return: The maximum number of rules the system should contain. -:rtype: int - -.. seealso:: :py:meth:`run`. - )pbdoc") - .def("max_rules", - py::overload_cast( - &libsemigroups::KnuthBendix::max_rules), - py::arg("val"), - R"pbdoc( -Set the maximum number of rules. - -This member function sets the (approximate) maximum number of rules that the -system should contain. If this is number is exceeded in calls to :py:meth:`run` -or -:py:meth:`knuth_bendix.by_overlap_length`, -then they will terminate and the system may not be confluent. - -By default this value is :py:obj:`POSITIVE_INFINITY`. - -:param val: The maximum number of rules. -:type val: int -:return: A reference to ``self``. - -.. seealso:: :py:meth:`run`. - )pbdoc") - .def("overlap_policy", - py::overload_cast<>(&KnuthBendix::overlap_policy, - py::const_), - R"pbdoc( -Return the overlap policy. - -This function returns the way that the length of an overlap of two words in the -system is measured. - -:Parameters: None -:return: The overlap policy. -:rtype: overlap - -.. seealso:: :py:class:`overlap`. - )pbdoc") - .def("overlap_policy", - py::overload_cast< - typename KnuthBendix::options::overlap>( - &KnuthBendix::overlap_policy), - py::arg("val"), - R"pbdoc( -Set the overlap policy. - -This function can be used to determine the way that the length of an overlap of -two words in the system is measured. - -:param val: The overlap policy. -:type val: overlap -:return: A reference to `self`. - -.. seealso:: :py:class:`overlap` - )pbdoc"); - ////////////////////////////////////////////////////////////////////////// - // Member functions for rules and rewriting - ////////////////////////////////////////////////////////////////////////// - // TODO: Delete or include. Do CongIntf need this functionality? - // kb.def("validate_word", - // &libsemigroups::KnuthBendix::validate_word, - // py::arg("w"), - // R"pbdoc( - // Check if every letter of a word is in the presentation's - // alphabet. - - // :param w: word to validate. - // :type w: ?? - // :return: (None) - // )pbdoc") - kb.def( - "presentation", - [](KnuthBendix& kb) { return kb.presentation(); }, - R"pbdoc( -Return the presentation defined by the rewriting system - -:Parameters: None -:return: The presentation -:rtype: Presentation - )pbdoc") - .def("number_of_active_rules", - &libsemigroups::KnuthBendix::number_of_active_rules, - R"pbdoc( -Return the current number of active rules. - -:Parameters: None -:return: The current number of active rules. -:rtype: int - )pbdoc") - .def("number_of_inactive_rules", - &libsemigroups::KnuthBendix::number_of_inactive_rules, - R"pbdoc( -Return the current number of inactive rules. - -:Parameters: None -:return: The current number of inactive rules. -:rtype: int - )pbdoc") - .def("total_rules", - &libsemigroups::KnuthBendix::total_rules, - R"pbdoc( -Return the number of rules that have been created - -Return the total number of Rule instances that have been created whilst whilst -the Knuth-Bendix algorithm has been running. Note that this is not the sum of -:py:meth:`number_of_active_rules` and :py:meth:`number_of_inactive_rules`, due -to the re-initialisation of rules where possible. - -:Parameters: None -:return: The total number of rules. -:rtype: int - )pbdoc") - .def( - "active_rules", - [](KnuthBendix& kb) { - auto rules = kb.active_rules(); - return py::make_iterator(rx::begin(rules), rx::end(rules)); - }, - R"pbdoc( -Return a copy of the active rules. - -This member function returns an iterator consisting of the pairs of strings -which represent the rewriting rules. The first entry in every such pair is -greater than the second according to the reduction ordering of the -:py:class:`KnuthBendix` instance. The rules are sorted -according to the reduction ordering used by the rewriting system, on the first -entry. - -:Parameters: None -:return: A copy of the currently active rules -:rtype: collections.abc.Iterator[(str, str)] - )pbdoc") - .def("rewrite", - &libsemigroups::KnuthBendix::rewrite, - py::arg("w"), - R"pbdoc( -Rewrite a word. - -Rewrites a copy of the word *w*, rewritten according to the current rules in the -KnuthBendix instance. - -:param w: the word to rewrite. -:type w: str -:return: A copy of the argument *w* after it has been rewritten. -:rtype: str - )pbdoc"); - ////////////////////////////////////////////////////////////////////////// - // Main member functions - ////////////////////////////////////////////////////////////////////////// - kb.def("confluent", - &libsemigroups::KnuthBendix::confluent, - R"pbdoc( -Check `confluence `_ of the current rules. - -:Parameters: None -:return: ``True`` if the :py:class:`KnuthBendix` - instance is confluent and ``False`` if it is not. -:rtype: bool - )pbdoc") - .def("confluent_known", - &libsemigroups::KnuthBendix::confluent_known, - R"pbdoc( -Check if the current system knows the state of confluence of the current rules. - -:Parameters: None -:return: ``True`` if the confluence of the rules in the - :py:class:`KnuthBendix` instance is known, and - ``False`` if it is not. -:rtype: bool - )pbdoc") - .def( - "gilman_graph", - [](KnuthBendix& kb) { - // TODO should different node types be possible? - WordGraph g = kb.gilman_graph(); - return g; - }, - py::return_value_policy::copy, - // REVIEW: Should WordGraph be formatted as code, or as text? - R"pbdoc( -Return the Gilman :py:class:`WordGraph`. - -The Gilman :py:class:`WordGraph` is a digraph where the labels of the paths from -the initial node (corresponding to the empty word) correspond to the short-lex -normal forms of the semigroup elements. - -The semigroup is finite if the graph is cyclic, and infinite otherwise. - -:Parameters: None -:return: The Gilman :py:class:`WordGraph`. -:rtype: WordGraph - -.. warning:: This will terminate when the - :py:class:`KnuthBendix` instance is reduced and - confluent, which might be never. - -.. seealso:: :py:meth:`number_of_normal_forms` and :py:meth:`normal_forms`. - )pbdoc") - .def("gilman_graph_node_labels", - &libsemigroups::KnuthBendix::gilman_graph_node_labels, - R"pbdoc( -Return the node labels of the Gilman :py:class:`WordGraph` - -Return the node labels of the Gilman :py:class:`WordGraph`, corresponding to the -unique prefixes of the left-hand sides of the rules of the rewriting system. - -:Parameters: None -:return: The node labels of the Gilman :py:class:`WordGraph` -:rtype: List[str] - -.. seealso:: :py:meth:`gilman_graph`. - )pbdoc"); - ////////////////////////////////////////////////////////////////////////// - // Attributes - ////////////////////////////////////////////////////////////////////////// - kb.def("number_of_classes", - &libsemigroups::KnuthBendix::number_of_classes, - cong_intf_doc_strings::number_of_classes) - .def("equal_to", - &libsemigroups::KnuthBendix::equal_to, - py::arg("u"), - py::arg("v"), - R"pbdoc( -Check if a pair of words are equivalent with respect to the system. - -:param u: a word over the generators of the semigroup. -:type u: List[int] -:param v: a word over the generators of the semigroup. -:type v: List[int] - -:return: ``True`` if the word *u* is equivalent to the word *v*, and ``False`` - otherwise. - )pbdoc") - .def("contains", - py::overload_cast( - &libsemigroups::KnuthBendix::contains), - py::arg("u"), - py::arg("v"), - cong_intf_doc_strings::contains) - .def("normal_form", - &libsemigroups::KnuthBendix::normal_form, - R"pbdoc()pbdoc"); - ////////////////////////////////////////////////////////////////////////// - // Inherited from CongruenceInterface - ////////////////////////////////////////////////////////////////////////// - kb.def("kind", - py::overload_cast<>(&libsemigroups::KnuthBendix::kind, - py::const_), - R"pbdoc( -The handedness of the congruence (left, right, or 2-sided). - -:return: - A congruence_kind. - )pbdoc") - .def("kind", - py::overload_cast( - &libsemigroups::KnuthBendix::kind)) - .def("number_of_generating_pairs", - &KnuthBendix::number_of_generating_pairs) - .def("add_pair", - py::overload_cast( - &libsemigroups::KnuthBendix::add_pair)) - .def("generating_pairs", &KnuthBendix::generating_pairs); - ////////////////////////////////////////////////////////////////////////// - // Helpers - ////////////////////////////////////////////////////////////////////////// - m.def( - "by_overlap_length", - [](KnuthBendix& kb) { - knuth_bendix::by_overlap_length(kb); - }, - R"pbdoc( -:sig=(kb: KnuthBendixRewriteTrie): -:only-document-once: -TODO - )pbdoc"); - m.def( - "normal_forms", - [](KnuthBendix& kb) { - return knuth_bendix::normal_forms(kb); - }, - R"pbdoc( -:sig=(kb: KnuthBendixRewriteTrie): -:only-document-once: -TODO - )pbdoc"); - m.def( - "non_trivial_classes", - [](KnuthBendix& kb1, KnuthBendix& kb2) { - return knuth_bendix::non_trivial_classes(kb1, kb2); - }, - R"pbdoc( -:sig=(kb1: KnuthBendixRewriteTrie, kb2: KnuthBendixRewriteTrie): -:only-document-once: -TODO - )pbdoc"); - m.def( - "is_reduced", - [](KnuthBendix& kb1) { - return knuth_bendix::is_reduced(kb1); - }, - R"pbdoc( -:sig=(kb: KnuthBendixRewriteTrie): -:only-document-once: -TODO - )pbdoc"); - // REVIEW should the the report guard be turned off for this? - m.def( - "redundant_rule", - [](Presentation const& p, std::chrono::nanoseconds t) { - return std::distance(p.rules.cbegin(), - knuth_bendix::redundant_rule(p, t)); - }, - R"pbdoc( -:sig=(p: Presentation, t: datetime.timedelta): -:only-document-once: -Return the index of the the left hand side of a redundant rule. - -Starting with the last rule in the presentation, this function attempts to -run the Knuth-Bendix algorithm on the rules of the presentation except for -the given omitted rule. For every such omitted rule, Knuth-Bendix is run for -the length of time indicated by the second parameter ``t`` and then it is -checked if the omitted rule can be shown to be redundant (rewriting both -sides of the omitted rule using the other rules using the output of the, not -necessarily finished, Knuth-Bendix algorithm). - -If the omitted rule can be shown to be redundant in this way, then the index -of its left hand side is returned. - -If no rule can be shown to be redundant in this way, then ``len(p.rules)`` -is returned. - -:warning: - The progress of the Knuth-Bendix algorithm may differ between different - calls to this function even if the parameters are identical. As such this - is non-deterministic, and may produce different results with the same - input. - -:param p: the presentation. -:type p: Presentation -:param t: time to run KnuthBendix for every omitted rule -:type t: datetime.timedelta - -:return: The index of a redundant rule (if any). - -.. doctest:: - - >>> from libsemigroups_pybind11 import knuth_bendix, presentation, Presentation - >>> from datetime import timedelta - >>> p = Presentation("ab") - >>> presentation.add_rule(p, "ab", "ba") - >>> presentation.add_rule(p, "bab", "abb") - >>> t = timedelta(seconds = 1) - >>> p.rules - ['ab', 'ba', 'bab', 'abb'] - >>> knuth_bendix.redundant_rule(p, t) - 2 -)pbdoc"); - m.def( - "is_obviously_infinite", - [](KnuthBendix& kb) { return is_obviously_infinite(kb); }, - R"pbdoc( -:sig=(kb: KnuthBendixRewriteTrie) -> bool: - -Function for checking if the quotient of a finitely presented -semigroup or monoid defined by a :any:`KnuthBendix` object is obviously infinite -or not. - -This function returns ``True`` if the quotient of the finitely presented -semigroup or monoid defined by the :any:`KnuthBendix` object *kb* is obviously -infinite; ``False`` is returned if it is not. - -:param kb: the :any:`KnuthBendix` instance. -:type kb: KnuthBendix - -:returns: - Whether or not the quotient defined by a :any:`KnuthBendix` instance is - obviously infinite. -:rtype: - bool - -.. note:: - If this function returns ``False``, it is still possible that the quotient - defined by the :any:`KnuthBendix` object *kb* is infinite. -)pbdoc"); - } - } // namespace - - void init_knuth_bendix(py::module& m) { - bind_knuth_bendix(m, "KnuthBendixRewriteFromLeft"); - bind_knuth_bendix(m, "KnuthBendixRewriteTrie"); - } -} // namespace libsemigroups diff --git a/src/old/todd-coxeter.cpp b/src/old/todd-coxeter.cpp deleted file mode 100644 index 1f207869..00000000 --- a/src/old/todd-coxeter.cpp +++ /dev/null @@ -1,581 +0,0 @@ -// -// libsemigroups - C++ library for semigroups and monoids -// Copyright (C) 2021-2024 James D. Mitchell -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -// C++ stl headers.... -#include // for array -#include // for nanoseconds -#include // for __base, function -#include // for initializer_list -#include // for string -#include // for allocator, shared_ptr -#include // for operator+, char_traits, basic_st... -#include // for vector - -// libsemigroups.... -#include // for congruence_kind -#include // for operator==, UNDEFINED, Undefined -#include // for to_string -#include // for KnuthBendix -#include // for Runner -#include // for ToddCoxeter, ToddCoxeter::option... -#include // for word_type - -// pybind11.... -#include -#include -#include // for class_, enum_, init, make_iterator -#include - -// libsemigroups_pybind11.... -#include "doc-strings.hpp" // for add_pair, class_index_to_word -#include "main.hpp" // for init_todd_coxeter - -namespace libsemigroups { - class FroidurePinBase; -} -namespace libsemigroups { - namespace fpsemigroup { - class KnuthBendix; - } -} // namespace libsemigroups - -namespace py = pybind11; - -namespace libsemigroups { - /* - void init_todd_coxeter(py::module& m) { - using sort_function_type - = std::function; - - py::class_ tc(m, "ToddCoxeter"); - - py::enum_(tc, - "order", - R"pbdoc( - The possible arguments for :py:meth:`standardize`. - - The values in this enum can be used as the argument for - :py:meth:`standardize` to specify which ordering should be used. The - normal forms for congruence classes are given with respect to one of - the orders specified by the values in this enum. - )pbdoc") - .value("none", - congruence::ToddCoxeter::order::none, - R"pbdoc( - No standardization has been done. - )pbdoc") - .value("shortlex", - congruence::ToddCoxeter::order::shortlex, - R"pbdoc( - Normal forms are the short-lex least word belonging to a given - congruence class. - )pbdoc") - .value("lex", - congruence::ToddCoxeter::order::lex, - R"pbdoc( - Normal forms are the lexicographical least word belonging to a given - congruence class. - )pbdoc") - .value("recursive", - congruence::ToddCoxeter::order::recursive, - R"pbdoc( - Normal forms are the recursive-path least word belonging to a given - congruence class. - )pbdoc"); - - py::enum_(tc, - "strategy_options", - R"pbdoc( - Values for defining the strategy. - - The values in this enum can be used as the argument for the method - :py:meth:`strategy` to specify which strategy should be used when - performing a coset enumeration. - )pbdoc") - .value("hlt", - congruence::ToddCoxeter::options::strategy::hlt, - R"pbdoc( - This value indicates that the HLT (Hazelgrove-Leech-Trotter) - strategy should be used. This is analogous to ACE's R-style. - )pbdoc") - .value("felsch", - congruence::ToddCoxeter::options::strategy::felsch, - R"pbdoc( - This value indicates that the Felsch strategy should be used. - This is analogous to ACE's C-style. - )pbdoc") - .value("random", - congruence::ToddCoxeter::options::strategy::random, - R"pbdoc( - This value indicates that a random combination of the HLT and - Felsch strategies should be used. A random strategy (and - associated options) are selected from one of the 10 options: - - 1. HLT + full lookahead + no deduction processing + - standardization - 2. HLT + full lookahead + deduction processing + - standardization - 3. HLT + full lookahead + no deduction processing + no - standardization - 4. HLT + full lookahead + deduction processing + no - standardization - 5. HLT + partial lookahead + no deduction processing + - standardization - 6. HLT + partial lookahead + deduction processing + - standardization - 7. HLT + partial lookahead + no deduction processing + no - standardization - 8. HLT + partial lookahead + deduction processing + no - standardization - 9. Felsch + standardization - 10. Felsch + no standardization - - and this strategy is then run for approximately the amount - of time specified by the setting :py:meth:`random_interval`. - )pbdoc"); - - py::enum_(tc, - "lookahead_options", - R"pbdoc( - Values for specifying the type of lookahead to perform. - - The values in this enum can be used as the argument for - :py:meth:`lookahead` to specify the type of lookahead that should be - performed when using the HLT strategy. - )pbdoc") - .value("full", - congruence::ToddCoxeter::options::lookahead::full, - R"pbdoc( - A *full* lookahead is one starting from the initial coset. - Full lookaheads are therefore sometimes slower but may - detect more coincidences than a partial lookahead. - )pbdoc") - .value("partial", - congruence::ToddCoxeter::options::lookahead::partial, - R"pbdoc( - A *partial* lookahead is one starting from the current coset. - Partial lookaheads are therefore sometimes faster but may not - detect as many coincidences as a full lookahead. - )pbdoc"); - - py::enum_( - tc, - "froidure_pin_options", - R"pbdoc( - Values for specifying whether to use relations or Cayley graph. - - The values in this enum can be used as the argument for - :py:meth:`froidure_pin_policy` to specify whether the - defining relations, or the left/right Cayley graph, of a - :py:class:`FroidurePin` instance, should be used in the coset - enumeration. - - If the number of classes in the congruence represented by a - :py:class:`ToddCoxeter` instance is relatively small, by some - definition, compared to the size of the semigroup represented by the - :py:class:`FroidurePin` instance, then the ``use_relations`` option - is often faster. If the number of classes is relatively large, then - ``use_cayley_graph`` is often faster. - - It is guaranteed that run will terminate in an amount of time - proportionate to the size of the input if the policy - ``use_cayley_graph`` is used, whereas the run time when using the - policy ``use_relations`` can be arbitrarily high regardless of the - size of the input. - )pbdoc") - .value("none", - congruence::ToddCoxeter::options::froidure_pin::none, - R"pbdoc( - No policy has been specified. - )pbdoc") - .value("use_relations", - congruence::ToddCoxeter::options::froidure_pin::use_relations, - R"pbdoc( - Use the relations of a :py:class:`FroidurePin` instance. - )pbdoc") - .value("use_cayley_graph", - congruence::ToddCoxeter::options::froidure_pin::use_cayley_graph, - R"pbdoc( - Use the left or right Cayley graph of a :py:class:`FroidurePin` - instance. - )pbdoc"); - - tc.def(py::init(), - py::arg("kind"), - R"pbdoc( - Construct from kind (left/right/2-sided) and options. - - Constructs an empty instance of an interface to a congruence of - type specified by the argument. - - :Parameters: - **kind** (congruence_kind) the handedness of the - congruence. - - :Complexity: Constant. - - .. seealso:: :py:meth:`set_number_of_generators` and - :py:meth:`add_pair`. - )pbdoc") - .def(py::init(), - py::arg("knd"), - py::arg("tc"), - R"pbdoc( - Construct from kind (left/right/2-sided) and - :py:class:`ToddCoxeter`. - - This constructor creates a new :py:class:`ToddCoxeter` instance - representing a left, right, or two-sided congruence over the - quotient semigroup represented by a :py:class:`ToddCoxeter` - instance. - - :Parameters: - **knd** (congruence_kind) the handedness of the - congruence. - - **tc** (ToddCoxeter) the :py:class:`ToddCoxeter` - representing the underlying semigroup - - :Raises: - `RuntimeError - `_ - if ``tc`` - is a left, or right, congruence, and - ``knd`` is not left, or not right, respectively. - )pbdoc") - .def(py::init(), - py::arg("knd"), - py::arg("kb"), - R"pbdoc( - Construct from kind (left/right/2-sided) and - :py:class:`KnuthBendix`. - - A constructor that creates a new :py:class:`ToddCoxeter` - instance representing a left, right, or two-sided congruence - over the semigroup represented by a :py:class:`KnuthBendix` - instance. - - :Parameters: - **knd** (congruence_kind) the handedness of the - congruence. - - **kb** (KnuthBendix) the :py:class:`KnuthBendix` - representing the underlying semigroup. - )pbdoc") - .def(py::init(), - R"pbdoc( - Copy constructor. - - Constructs a complete copy of ``that``, including all of the - settings, table, defining relations, and generating pairs. - - :Parameters: - **that** (ToddCoxeter) the ToddCoxeter instance to - copy. )pbdoc") .def(py::init>(), R"pbdoc( Construct from kind - (left/right/2-sided) and FroidurePin. - - This constructor creates a :py:class:`ToddCoxeter` instance - representing a left, right, or two-sided congruence over the - semigroup represented by a :py:class:`FroidurePin` - object. - - :Parameters: - **knd** (congruence_kind) the kind of the - congruence being constructed - - **fp** (FroidurePin) the semigroup over which the - congruence is to be defined. )pbdoc") - // TODO(later) more of constructors? - .def("__repr__", - [](congruence::ToddCoxeter const& tc) { - auto n = (tc.number_of_generators() == UNDEFINED - ? "-" - : detail::to_string(tc.number_of_generators())); - - return std::string(""; - }) - .def("set_number_of_generators", - &congruence::ToddCoxeter::set_number_of_generators, - py::arg("n"), - cong_intf_doc_strings::set_number_of_generators) - .def( - "number_of_generators", - [](congruence::ToddCoxeter const& tc) { - return tc.number_of_generators(); - }, - cong_intf_doc_strings::number_of_generators) - .def("add_pair", - py::overload_cast( - &congruence::ToddCoxeter::add_pair), - py::arg("u"), - py::arg("v"), - cong_intf_doc_strings::add_pair) - .def( - "number_of_generating_pairs", - [](congruence::ToddCoxeter const& tc) { - return tc.number_of_generating_pairs(); - }, - cong_intf_doc_strings::number_of_generating_pairs) - .def("froidure_pin_policy", - py::overload_cast( - &congruence::ToddCoxeter::froidure_pin_policy), - R"pbdoc( - Sets the value of the Froidure-Pin policy specified by the - argument :py:obj:`ToddCoxeter.froidure_pin_options`. - )pbdoc") - .def( - "froidure_pin_policy", - [](congruence::ToddCoxeter const& x) { - return x.froidure_pin_policy(); - }, - R"pbdoc( - Gets the value of the Froidure-Pin policy. - )pbdoc") - .def("lookahead", - py::overload_cast( - &congruence::ToddCoxeter::lookahead), - R"pbdoc( - Sets the type of lookahead to be used when using the HLT - strategy. )pbdoc") .def("lower_bound", - py::overload_cast(&congruence::ToddCoxeter::lower_bound), - R"pbdoc( - Sets a lower bound for the number of classes of the congruence - represented by a ToddCoxeter instance. - )pbdoc") - .def( - "next_lookahead", - py::overload_cast(&congruence::ToddCoxeter::next_lookahead), - R"pbdoc( - If the number of cosets active exceeds the value set by this - function, then a lookahead, of the type set by lookahead, is triggered. - )pbdoc") - .def("save", - py::overload_cast(&congruence::ToddCoxeter::save), - R"pbdoc( - If the argument of this function is ``True`` and the HLT - strategy is being used, then deductions are processed during the - enumeration. - )pbdoc") - .def("standardize", - py::overload_cast(&congruence::ToddCoxeter::standardize), - R"pbdoc( - If the argument of this function is ``True``, then the coset - table is standardized (according to the short-lex order) during the coset - enumeration. - )pbdoc") - .def("strategy", - (congruence::ToddCoxeter::options::strategy( - congruence::ToddCoxeter::*)() const) - & congruence::ToddCoxeter::strategy, - R"pbdoc( - Returns the value of the strategy used during the coset - enumeration. - )pbdoc") - .def("strategy", - py::overload_cast( - &congruence::ToddCoxeter::strategy), - R"pbdoc( - Set the strategy used during the coset enumeration can be - specified using this function. - )pbdoc") - .def("random_interval", - (congruence::ToddCoxeter - & (congruence::ToddCoxeter::*) (std::chrono::nanoseconds)) - & congruence::ToddCoxeter::random_interval, - R"pbdoc( - Sets the duration in nanoseconds that a given randomly selected - strategy will run for, when using the random strategy - (:py:obj:`ToddCoxeter.strategy_options.random`). - )pbdoc") - .def("sort_generating_pairs", - py::overload_cast( - &congruence::ToddCoxeter::sort_generating_pairs), - py::arg("func"), - R"pbdoc( - Sorts all existing generating pairs according to the binary - function func. - - :param func: - a binary predicate that defines a linear order on the relations - in a :py:class:`ToddCoxeter` instance. :type func: Callable[], bool )pbdoc") - .def("random_shuffle_generating_pairs", - &congruence::ToddCoxeter::random_shuffle_generating_pairs, - R"pbdoc( - Randomly shuffle all existing generating pairs. - )pbdoc") - .def("report_every", - (void(congruence::ToddCoxeter::*)(std::chrono::nanoseconds)) - & Runner::report_every, - py::arg("t"), - runner_doc_strings::report_every) - .def("report", - &congruence::ToddCoxeter::report, - runner_doc_strings::report) - .def("report_why_we_stopped", - &congruence::ToddCoxeter::report_why_we_stopped, - runner_doc_strings::report_why_we_stopped) - .def("kill", &congruence::ToddCoxeter::kill, runner_doc_strings::kill) - .def("run", &congruence::ToddCoxeter::run, runner_doc_strings::run) - .def("run_for", - (void(congruence::ToddCoxeter::*)(std::chrono::nanoseconds)) - & Runner::run_for, - py::arg("t"), - runner_doc_strings::run_for) - .def("run_until", - (void(congruence::ToddCoxeter::*)(std::function&)) - & Runner::run_until, - py::arg("func"), - runner_doc_strings::run_until) - .def("less", - &congruence::ToddCoxeter::less, - py::arg("u"), - py::arg("v"), - cong_intf_doc_strings::less) - .def("const_contains", - &congruence::ToddCoxeter::const_contains, - py::arg("u"), - py::arg("v"), - cong_intf_doc_strings::const_contains) - .def("contains", - &congruence::ToddCoxeter::contains, - py::arg("u"), - py::arg("v"), - cong_intf_doc_strings::contains) - .def("empty", - &congruence::ToddCoxeter::empty, - R"pbdoc( - Returns ``True`` if there are no relations or generating pairs - in the ToddCoxeter instance, and the number of active cosets is - 1 (the minimum possible). - )pbdoc") - .def("number_of_classes", - &congruence::ToddCoxeter::number_of_classes, - cong_intf_doc_strings::number_of_classes) - .def("number_of_non_trivial_classes", - &congruence::ToddCoxeter::number_of_non_trivial_classes, - cong_intf_doc_strings::number_of_non_trivial_classes) - .def("reserve", - &congruence::ToddCoxeter::reserve, - R"pbdoc( - Reserves the capacity specified by the argument in the data - structures for cosets used in a ToddCoxeter instance. - )pbdoc") - .def("shrink_to_fit", - &congruence::ToddCoxeter::shrink_to_fit, - R"pbdoc( - Release all memory used to store free cosets, and any other - unnecessary data if the enumeration is finished. )pbdoc") - .def("quotient_froidure_pin", - &congruence::ToddCoxeter::quotient_froidure_pin, - cong_intf_doc_strings::quotient_froidure_pin) - .def("has_quotient_froidure_pin", - &congruence::ToddCoxeter::has_quotient_froidure_pin, - cong_intf_doc_strings::has_quotient_froidure_pin) - .def("parent_froidure_pin", - &congruence::ToddCoxeter::parent_froidure_pin, - cong_intf_doc_strings::parent_froidure_pin) - .def( - "has_parent_froidure_pin", - [](congruence::ToddCoxeter const& tc) { - return tc.has_parent_froidure_pin(); - }, - cong_intf_doc_strings::has_parent_froidure_pin) - .def("is_quotient_obviously_finite", - &congruence::ToddCoxeter::is_quotient_obviously_finite, - cong_intf_doc_strings::is_quotient_obviously_finite) - .def("is_quotient_obviously_infinite", - &congruence::ToddCoxeter::is_quotient_obviously_infinite, - cong_intf_doc_strings::is_quotient_obviously_infinite) - .def("word_to_class_index", - &congruence::ToddCoxeter::word_to_class_index, - py::arg("w"), - cong_intf_doc_strings::word_to_class_index) - .def("class_index_to_word", - &congruence::ToddCoxeter::class_index_to_word, - py::arg("i"), - cong_intf_doc_strings::class_index_to_word) - .def( - "kind", - [](congruence::ToddCoxeter const& tc) { return tc.kind(); }, - cong_intf_doc_strings::kind) - .def("complete", - &congruence::ToddCoxeter::complete, - R"pbdoc( - Returns ``True`` if the coset table is complete, and ``False`` if - it is not. )pbdoc") .def( "compatible", - [](congruence::ToddCoxeter const& tc) { return tc.compatible(); }, - R"pbdoc( - Returns ``True`` if the coset table is compatible with the - relations and generating pairs used to create this, and - ``False`` if it is not. - )pbdoc") - .def("dead", &congruence::ToddCoxeter::dead, runner_doc_strings::dead) - .def("finished", - &congruence::ToddCoxeter::finished, - runner_doc_strings::finished) - .def("timed_out", - &congruence::ToddCoxeter::timed_out, - runner_doc_strings::timed_out) - .def("stopped_by_predicate", - &congruence::ToddCoxeter::stopped_by_predicate, - runner_doc_strings::stopped_by_predicate) - .def("is_standardized", - &congruence::ToddCoxeter::is_standardized, - R"pbdoc( - Returns ``True`` if the :py:class:`ToddCoxeter` instance is - standardized. - )pbdoc") - .def("standardize", - py::overload_cast( - &congruence::ToddCoxeter::standardize), - R"pbdoc( - If the argument of this function is ``True``, then the coset - table is standardized (according to the short-lex order) during - the coset enumeration. - )pbdoc") - .def( - "generating_pairs", - [](congruence::ToddCoxeter const& tc) { - return py::make_iterator(tc.cbegin_generating_pairs(), - tc.cend_generating_pairs()); - }, - cong_intf_doc_strings::generating_pairs) - .def( - "non_trivial_classes", - [](congruence::ToddCoxeter& tc) { - return py::make_iterator(tc.cbegin_ntc(), tc.cend_ntc()); - }, - cong_intf_doc_strings::non_trivial_classes) - .def( - "normal_forms", - [](congruence::ToddCoxeter& tc) { - return py::make_iterator(tc.cbegin_normal_forms(), - tc.cend_normal_forms()); - }, - R"pbdoc( - Returns an iterator to the normal forms of the congruence - represented by an instance of :py:class:`ToddCoxeter`. - )pbdoc") - .def("to_gap_string", - &congruence::ToddCoxeter::to_gap_string, - R"pbdoc( - Returns a string containing a GAP definition of the finitely - presented semigroup represented by a ``ToddCoxeter`` instance. - - :parameters: None - - :returns: A string - )pbdoc"); - } -*/ -} // namespace libsemigroups diff --git a/src/paths.cpp b/src/paths.cpp index ae2828a0..60094878 100644 --- a/src/paths.cpp +++ b/src/paths.cpp @@ -155,7 +155,7 @@ return value of :any:`count` decreases by ``1``). R"pbdoc( Get the current target node of the path labelled by :any:`Paths.get`. This function returns the current target node of the path labelled by -:any:`Paths.get` . If there is no such path (because, for example, the source +:any:`Paths.get`. If there is no such path (because, for example, the source node hasn't been defined, then :any:`UNDEFINED` is returned). :returns: @@ -482,7 +482,7 @@ return value of :any:`count` decreases by ``1``). R"pbdoc( Get the current target node of the path labelled by :any:`ReversiblePaths.get`. This function returns the current target node of the path labelled by -:any:`ReversiblePaths.get` . If there is no such path (because, for example, +:any:`ReversiblePaths.get`. If there is no such path (because, for example, the source node hasn't been defined, then :any:`UNDEFINED` is returned). :returns: diff --git a/src/pbr.cpp b/src/pbr.cpp index b53574db..9a32b479 100644 --- a/src/pbr.cpp +++ b/src/pbr.cpp @@ -65,10 +65,10 @@ Construct from adjacencies ``1`` to ``n`` and ``-1`` to ``-n``. The parameters *left* and *right* should be containers of ``n`` vectors of integer values, so that the vector in position ``i`` of *left* is the list of points adjacent to ``i`` in the :any:`PBR`, and the vector in position ``i`` of -*right* is the list of points adjacent to ``n + i`` in the :any:`PBR` . +*right* is the list of points adjacent to ``n + i`` in the :any:`PBR`. A negative value ``i`` corresponds to ``n - i``. -:param left: container of adjacencies of ``1`` to ``n`` +:param left: container of adjacencies of ``1`` to ``n`` :type left: List[List[int]] :param right: container of adjacencies of ``n + 1`` to ``2n``. diff --git a/src/present.cpp b/src/present.cpp index 7018fde0..e5e42778 100644 --- a/src/present.cpp +++ b/src/present.cpp @@ -67,7 +67,7 @@ For an implementation of presentations for semigroups or 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 +``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:`PresentationStrings` class also provides some checks that the rules really define a presentation, (i.e. it's consistent with its @@ -429,7 +429,7 @@ alphabet of *p* , and where :math:`e` is the second parameter. Add rules for inverses. The letter *a* with index ``i`` in *vals* is the inverse of the letter in -``alphabet()`` with index ``i`` . The rules added are :math:`a_ib_i = e` where +``alphabet()`` with index ``i``. The rules added are :math:`a_ib_i = e` where the alphabet is :math:`\{a_1, \ldots, a_n\}` ; the 2nd parameter *vals* is :math:`\{b_1, \ldots, b_n\}` ; and :math:`e` is the 3rd parameter. @@ -768,7 +768,7 @@ Return the longest common subword of the rules. If it is possible to find a subword :math:`w` of the rules :math:`u_1 = v_1, \ldots, u_n = v_n` such that the introduction of a new generator :math:`z` and the relation :math:`z = w` reduces the :any`presentation.length` of the -presentation, then this function returns the longest such word :math:`w` . +presentation, then this function returns the longest such word :math:`w`. If no such word can be found, then a word of length :math:`0` is returned. :param p: the presentation. @@ -940,7 +940,7 @@ right-hand side are identical. Replace non-overlapping instances of a subword by another word. If *existing* and *replacement* are words, then this function replaces every -non-overlapping instance of *existing* in every rule by *replacement* . The +non-overlapping instance of *existing* in every rule by *replacement*. The presentation *p* is changed in-place. :param p: the presentation . @@ -965,7 +965,7 @@ Replace instances of a word on either side of a rule by another word. If *existing* and *replacement* are words, then this function replaces every instance of *existing* in every rule of the form *existing* :math:`= w` or :math:`w =` -*existing*, with the word *replacement* . The presentation *p* is changed in-place. +*existing*, with the word *replacement*. The presentation *p* is changed in-place. :param p: the presentation. :type p: PresentationStrings @@ -990,7 +990,7 @@ instance of *existing* in every rule of the form *existing* :math:`= w` or :math Replace non-overlapping instances of a word with a new generator via const reference. This function replaces every non-overlapping instance (from left to right) of -*w* in every rule, adds a new generator :math:`z` , and the rule :math:`w = z` . +*w* in every rule, adds a new generator :math:`z`, and the rule :math:`w = z`. The new generator and rule are added even if *w* is not a subword of any rule. :param p: the presentation. @@ -1190,11 +1190,11 @@ are created by taking quotients of free semigroups or monoids. Validate if vals act as semigroup inverses in p. Check if the values in *vals* act as semigroup inverses for the letters of the -alphabet of *p* . Specifically, it checks that the :math:`i` th value in *vals* +alphabet of *p*. Specifically, it checks that the :math:`i` th value in *vals* acts as an inverse for the :math:`i` th value in ``p.alphabet()``. Let :math:`x_i` be the :math:`i` th letter in ``p.alphabet()`` , and suppose -that :math:`x_i=v_j` is in the :math:`j` th position of *vals* . This function +that :math:`x_i=v_j` is in the :math:`j` th position of *vals*. This function checks that :math:`v_i = x_j` , and therefore that :math:`(x_i^{-1})^{-1} = x`. :param p: the presentation. diff --git a/src/runner.cpp b/src/runner.cpp index c5d28382..ee8b979b 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -114,7 +114,7 @@ greater than the value of :any:`report_every()`. If ``True`` is returned, then R"pbdoc( Set the minimum elapsed time between reports. -This function can be used to specify at run time the minimum elapsed time between two calls to :any:`report()` that will return ``True`` . If :any:`report()` returns ``True`` at time ``s`` , then :any:`report()` will only return ``True`` again after at least time ``s + t`` has elapsed. +This function can be used to specify at run time the minimum elapsed time between two calls to :any:`report()` that will return ``True``. If :any:`report()` returns ``True`` at time ``s`` , then :any:`report()` will only return ``True`` again after at least time ``s + t`` has elapsed. :param val: the amount of time between reports. :type val: datetime.timedelta @@ -195,7 +195,7 @@ Set the last report time point to now. R"pbdoc( Set the prefix string for reporting. -This function sets the return value of :any:`report_prefix()` to (a copy of) the argument ``val`` . Typically this prefix should be the name of the algorithm being run at the outmost level. +This function sets the return value of :any:`report_prefix()` to (a copy of) the argument ``val``. Typically this prefix should be the name of the algorithm being run at the outmost level. :param val: the new value of the report prefix. :type val: str @@ -228,9 +228,9 @@ Abstract class for derived classes that run an algorithm. Many of the classes in ``libsemigroups`` implementing the algorithms, that are the reason for the existence of this library, are derived from -:any:`Runner` . The :any:`Runner` class exists to collect various common +:any:`Runner`. The :any:`Runner` class exists to collect various common tasks required by such a derived class with a possibly long running -:any:`run` . These common tasks include: +:any:`run`. These common tasks include: * running for a given amount of time (:any:`run_for`) * running until a nullary predicate is true (:any:`run_until`) @@ -442,7 +442,7 @@ was running it was stopped by a call to the nullary predicate passed to Check if the runner is currently running for a particular length of time. If the :any:`Runner` is currently running because its member function :any:`run_for` has been invoked, then this function returns -``True`` . Otherwise, ``False`` is returned. +``True``. Otherwise, ``False`` is returned. :complexity: Constant. @@ -459,7 +459,7 @@ function :any:`run_for` has been invoked, then this function returns Check if the runner is currently running until a nullary predicate returns true. If the :any:`Runner` is currently running because its member function :any:`run_until` has been invoked, then this function -returns ``True`` . Otherwise, ``False`` is returned. +returns ``True``. Otherwise, ``False`` is returned. :complexity: Constant. diff --git a/src/todd-coxeter.cpp b/src/todd-coxeter.cpp index e1b914a7..9bde1ec9 100644 --- a/src/todd-coxeter.cpp +++ b/src/todd-coxeter.cpp @@ -90,12 +90,12 @@ the execution of (any version of) the Todd-Coxeter algorithm. with 10753/2097153 active/nodes> >>> tc.word_graph() - >>> it = todd_coxeter.str_normal_forms(tc) + >>> it = todd_coxeter.normal_forms(tc, Word=str) >>> [next(it) for _ in range(10)] ['a', 'b', 'c', 'd', 'bc', 'bd', 'cb', 'db', 'bcb', 'bdb'] >>> tc.standardize(Order.lex) True - >>> it = todd_coxeter.str_normal_forms(tc) + >>> it = todd_coxeter.normal_forms(tc, Word=str) >>> [next(it) for _ in range(10)] ['a', 'ab', 'abc', 'abcb', 'abcbc', 'abcbcb', 'abcbcbc', 'abcbcbcb', 'abcbcbcbc', 'abcbcbcbcb'] )pbdoc"); @@ -287,21 +287,21 @@ Copy a :any:`ToddCoxeter` object. R"pbdoc( :sig=(self: ToddCoxeter, knd: congruence_kind, p: Presentation) -> None: -Construct from :any:`congruence_kind` and :any:`Presentation`. +Construct from :any:`congruence_kind` and :any:`PresentationStrings`. This function constructs a :any:`ToddCoxeter` instance representing a congruence of kind *knd* over the semigroup or monoid defined by the presentation *p*. The type of the words in *p* can be anything, but will be converted into ``List[int]``. This means that if the input presentation uses :any:`str`, for example, as the word type, then this presentation is -converted into a :any:`Presentation` using ``List[int]`` instead. This +converted into a :any:`PresentationStrings` using ``List[int]`` instead. This converted presentation can be recovered using :any:`ToddCoxeter.presentation`. :param knd: the kind (onesided or twosided) of the congruence. :type knd: congruence_kind :param p: the presentation. -:type p: Presentation +:type p: PresentationStrings :raises LibsemigroupsError: if *p* is not valid.)pbdoc"); @@ -379,7 +379,7 @@ had been newly constructed from *knd* and *p*. :type knd: :any:`congruence_kind` :param p: the presentation. -:type p: Presentation +:type p: PresentationStrings :returns: ``self``. :rtype: ToddCoxeter @@ -1391,12 +1391,12 @@ produce different output words. Get the current possible spanning tree of the underlying word graph. This function returns a const reference to the current value of a possible -spanning tree (a :any:`Forest` ) for the underlying :any:`WordGraph` (returned -by :any:`current_word_graph` ). This spanning tree is only populated during +spanning tree (a :any:`Forest`) for the underlying :any:`WordGraph` (returned +by :any:`current_word_graph`). This spanning tree is only populated during calls to :any:`standardize` and as such might contain nothing, or a spanning -tree of a previous value of :any:`current_word_graph` . Some care should be +tree of a previous value of :any:`current_word_graph`. Some care should be used with the return value of this function, and it might be better to use the -function :any:`spanning_tree` , which has none of these limitation.If +function :any:`spanning_tree`, which has none of these limitation. If :any:`Runner.finished` returns ``True``, and :any:`standardize` has been called prior to a call to this function, then the returned :any:`Forest` will represent a valid spanning tree for the :any:`WordGraph` returned by @@ -1513,24 +1513,24 @@ The return value of this function indicates the following: and: - the return value of :any:`reduce` will essentially arbitrary; - - the return values of :any:`todd_coxeter.word_normal_forms` or - :any:`todd_coxeter.str_normal_forms` will be essentially arbitrary; + - the return values of :any:`todd_coxeter.normal_forms` or + :any:`todd_coxeter.normal_forms` will be essentially arbitrary; - the classes of the congruence will be indexed in an arbitrary order; - :any:`Order.shortlex` implies that: - the return value of :any:`reduce` will be the short-lex least word belonging to a given congruence class; - - the return values of :any:`todd_coxeter.word_normal_forms` and - :any:`todd_coxeter.str_normal_forms` will be in + - the return values of :any:`todd_coxeter.normal_forms` and + :any:`todd_coxeter.normal_forms` will be in short-lex order; - the classes of the congruence will be indexed in short-lex order on the short-lex least word; - :any:`Order.lex` implies that: - - the return values of :any:`todd_coxeter.word_normal_forms` and - :any:`todd_coxeter.str_normal_forms` will be ordered lexicographically. + - the return values of :any:`todd_coxeter.normal_forms` and + :any:`todd_coxeter.normal_forms` will be ordered lexicographically. - the return values of :any:`reduce` and the indexes of class are essentially arbitrary because there is not necessarily a lexicographically least word in every class; @@ -1539,8 +1539,8 @@ The return value of this function indicates the following: - the return value of :any:`reduce` will be the recursive path least word belonging to a given congruence class; - - the return values of :any:`todd_coxeter.word_normal_forms` and - :any:`todd_coxeter.str_normal_forms` will be ordered by the + - the return values of :any:`todd_coxeter.normal_forms` and + :any:`todd_coxeter.normal_forms` will be ordered by the recursive path order; - the classes of the congruence will be indexed in recursive path order on the recursive path least word. @@ -1719,59 +1719,17 @@ word graph is complete, and so the return value is never :any:`UNDEFINED`. :sig=(self: ToddCoxeter, w: List[int] | str) -> int: )pbdoc"); - // TODO this causes a full enumeration - thing.def( - "current_word_of", - [](ToddCoxeter& self, size_t i) { - return todd_coxeter::current_word_of(self, i); - }, - py::arg("i"), - R"pbdoc( -Returns a current word representing a class with given index. - -This function returns the current word representing the class with index *i*. -No enumeration is triggered by calls to this function, but -:any:`current_word_graph` is standardized (using :any:`Order.shortlex`) if it -is not already standardized. The output word is obtained by following a path in -:any:`current_spanning_tree` from the node corresponding to index *i* back to -the root of that tree. - -:returns: The word representing the *i*-th class. -:rtype: List[int] - -:param i: the index of the class. -:type i: int - -:raises LibsemigroupsError: if *i* is out of bounds. -)pbdoc"); - - thing.def( - "current_str_of", - [](ToddCoxeter& self, size_t i) { - return todd_coxeter::current_word_of(self, i); - }, - py::arg("i"), - R"pbdoc( -Returns a current word representing a class with given index. - -This function returns the current word representing the class with index *i*. -No enumeration is triggered by calls to this function, but -:any:`current_word_graph` is standardized (using :any:`Order.shortlex`) if it -is not already standardized. The output word is obtained by following a path in -:any:`current_spanning_tree` from the node corresponding to index *i* back to -the root of that tree. - -:returns: The word representing the *i*-th class. -:rtype: str - -:param i: the index of the class. -:type i: int + // FIXME(0) this causes a full enumeration + thing.def("_current_word_of", [](ToddCoxeter& self, size_t i) { + return todd_coxeter::current_word_of(self, i); + }); -:raises LibsemigroupsError: if *i* is out of bounds. -)pbdoc"); + thing.def("_current_str_of", [](ToddCoxeter& self, size_t i) { + return todd_coxeter::current_word_of(self, i); + }); thing.def( - "word_of", + "_word_of", [](ToddCoxeter& self, size_t i) { return todd_coxeter::word_of(self, i); }, @@ -1793,7 +1751,7 @@ to index *i* back to the root of that tree. :raises LibsemigroupsError: if *i* is out of bounds. )pbdoc"); thing.def( - "str_of", + "_str_of", [](ToddCoxeter& self, size_t i) { return todd_coxeter::word_of(self, i); }, @@ -1812,72 +1770,27 @@ to index *i* back to the root of that tree. :param i: the index of the class. :type i: int -:raises LibsemigroupsError: if *i* is out of bounds. +:raises LibsemigroupsError: if *i* is out of bounds. )pbdoc"); //////////////////////////////////////////////////////////////////////// // Helpers //////////////////////////////////////////////////////////////////////// - m.def( - "str_class_by_index", - [](ToddCoxeter& tc, size_t n) { - auto c = todd_coxeter::class_by_index(tc, n); - // is this ok, does c somehow get copied into the iterator? - return py::make_iterator(rx::begin(c), rx::end(c)); - }, - py::arg("tc"), - py::arg("n"), - R"pbdoc( -Returns an iterator yielding every string in the congruence class with given -index. - -This function returns an iterator yielding every string belonging to the -class with index *n* in the congruence represented by the :any:`ToddCoxeter` -instance *tc*. Calls to this function trigger a full enumeration of *tc*. - -:param tc: the ToddCoxeter instance. -:type tc: ToddCoxeter - -:param n: the index of the class. -:type n: int - -:returns: A iterator yielding the class with index *n*. -:rtype: Iterator[str] - -:raises LibsemigroupsError: - if *n* is greater than or equal to ``tc.number_of_classes()``. -)pbdoc"); - - m.def( - "word_class_by_index", - [](ToddCoxeter& tc, size_t n) { - auto c = todd_coxeter::class_by_index(tc, n); - // is this ok, does c somehow get copied into the iterator? - return py::make_iterator(rx::begin(c), rx::end(c)); - }, - py::arg("tc"), - py::arg("n"), - R"pbdoc( -Returns an iterator yielding every word ``List[int]`` in the congruence class -with given index. - -This function returns an iterator yielding every word belonging to the -class with index *n* in the congruence represented by the :any:`ToddCoxeter` -instance *tc*. Calls to this function trigger a full enumeration of *tc*. - -:param tc: the ToddCoxeter instance. -:type tc: ToddCoxeter - -:param n: the index of the class. -:type n: int - -:returns: A iterator yielding the class with index *n*. -:rtype: Iterator[List[int]] + // The next 2 functions are documented in the wrapper in + // libsemigroups_pybind11/todd_coxeter.py, because they have the + // additional kwarg Word to specify the output type. + m.def("_str_class_by_index", [](ToddCoxeter& tc, size_t n) { + auto c = todd_coxeter::class_by_index(tc, n); + // is this ok, does c somehow get copied into the iterator? + return py::make_iterator(rx::begin(c), rx::end(c)); + }); -:raises LibsemigroupsError: - if *n* is greater than or equal to ``tc.number_of_classes()``. - )pbdoc"); + m.def("_word_class_by_index", [](ToddCoxeter& tc, size_t n) { + auto c = todd_coxeter::class_by_index(tc, n); + // is this ok, does c somehow get copied into the iterator? + return py::make_iterator(rx::begin(c), rx::end(c)); + }); m.def( "class_of", @@ -1892,12 +1805,12 @@ instance *tc*. Calls to this function trigger a full enumeration of *tc*. :sig=(tc: ToddCoxeter, w: List[int] | str) -> Iterator[List[int] | str]: :only-document-once: -Returns an iterator yielding containing every word (of the same type as *w*) in +Returns an iterator yielding every word (of the same type as *w*) in the congruence class of the given word *w*. -This function returns a range object containing every word in belonging to the -same class as the input word *w* in the congruence represented by the -:any:`ToddCoxeter` instance *tc* . Calls to this function trigger a full +This function returns an iterator yielding every word in belonging to the same +class as the input word *w* in the congruence represented by the +:any:`ToddCoxeter` instance *tc*. Calls to this function trigger a full enumeration of *tc*. :param tc: the ToddCoxeter instance. @@ -1926,55 +1839,18 @@ enumeration of *tc*. R"pbdoc( :sig=(tc: ToddCoxeter, w: List[int] | str) -> Iterator[List[int] | str]:)pbdoc"); - m.def( - "todd_coxeter_word_normal_forms", - [](ToddCoxeter& tc) { - auto nf = todd_coxeter::normal_forms(tc); - return py::make_iterator(rx::begin(nf), rx::end(nf)); - }, - py::arg("tc"), - R"pbdoc( -:sig=(tc: ToddCoxeter) -> Iterator[List[int]]: - -Returns an iterator yielding normal forms. - -This function returns an iterator yielding normal forms of the classes of -the congruence represented by an instance of :any:`ToddCoxeter`. The order of -the classes, and the normal forms, that are returned are controlled by -:any:`ToddCoxeter.standardize`. This function triggers a full enumeration of -``tc``. - -:param tc: the ToddCoxeter instance. -:type tc: ToddCoxeter - -:returns: An iterator yielding normal forms. -:rtype: Iterator[List[int]] -)pbdoc"); - - m.def( - "todd_coxeter_str_normal_forms", - [](ToddCoxeter& tc) { - auto nf = todd_coxeter::normal_forms(tc); - return py::make_iterator(rx::begin(nf), rx::end(nf)); - }, - py::arg("tc"), - R"pbdoc( -:sig=(tc: ToddCoxeter) -> Iterator[str]: - -Returns an iterator yielding normal forms. - -This function returns an iterator yielding normal forms of the classes of -the congruence represented by an instance of :any:`ToddCoxeter`. The order of -the classes, and the normal forms, that are returned are controlled by -:any:`ToddCoxeter.standardize`. This function triggers a full enumeration of -``tc``. - -:param tc: the ToddCoxeter instance. -:type tc: ToddCoxeter + // The next 2 functions are documented in the wrapper in + // libsemigroups_pybind11/todd_coxeter.py, because they have the + // additional kwarg Word to specify the output type. + m.def("todd_coxeter_word_normal_forms", [](ToddCoxeter& tc) { + auto nf = todd_coxeter::normal_forms(tc); + return py::make_iterator(rx::begin(nf), rx::end(nf)); + }); -:returns: An iterator yielding normal forms. -:rtype: Iterator[str] -)pbdoc"); + m.def("todd_coxeter_str_normal_forms", [](ToddCoxeter& tc) { + auto nf = todd_coxeter::normal_forms(tc); + return py::make_iterator(rx::begin(nf), rx::end(nf)); + }); m.def("todd_coxeter_is_non_trivial", &todd_coxeter::is_non_trivial, @@ -2073,7 +1949,7 @@ of the words in the list *words* induced by the :any:`ToddCoxeter` instance Partition a list of words. This function returns the classes in the partition of the words in the input -list *words* induced by the :any:`ToddCoxeter` instance *tc* . This function +list *words* induced by the :any:`ToddCoxeter` instance *tc*. This function triggers a full enumeration of *tc*. :param tc: the ToddCoxeter instance. @@ -2129,7 +2005,7 @@ be redundant in this way, then this rule is returned If no rule can be shown to be redundant in this way, then ``None`` is returned. :param p: the presentation. -:type p: Presentation +:type p: PresentationStrings :param t: time to run Todd-Coxeter for every omitted rule. :type t: timedelta diff --git a/src/transf.cpp b/src/transf.cpp index ee457fdb..38c9420f 100644 --- a/src/transf.cpp +++ b/src/transf.cpp @@ -473,7 +473,7 @@ Class for representing permutations on up to ``2 ** 32`` points. A *permutation* :math:`f` is an injective transformation defined on the whole of :math:`\{0, 1, \ldots, n - 1\}` for some integer :math:`n` called the -*degree* of :math:`f` . A permutation is stored as a vector of the images of +*degree* of :math:`f`. A permutation is stored as a vector of the images of :math:`(0, 1, \ldots, n - 1)` , i.e. :math:`((0)f, (1)f, \ldots, (n - 1)f)`. .. doctest:: diff --git a/src/ukkonen.cpp b/src/ukkonen.cpp index ba1bd9b4..45c3c15f 100644 --- a/src/ukkonen.cpp +++ b/src/ukkonen.cpp @@ -104,7 +104,7 @@ of the unique letters added to the end of words in the suffix tree. Check and add a word to the suffix tree. -Calling this first checks that none of the letters in *w* is equal to any of +Calling this first checks that none of the letters in *w* is equal to any of the existing unique letters. It then invokes Ukkonen's algorithm to add the given word to the suffix tree (if it is not already contained in the tree). If an identical word is already in the tree, then this @@ -121,7 +121,7 @@ If *w* is empty, then this function does nothing. :complexity: Linear in the length of *w*. .. seealso:: - + :any:`Ukkonen.throw_if_contains_unique_letter`. )pbdoc"); @@ -145,7 +145,7 @@ Add all words in a list to an :any:`Ukkonen` object. :raises LibsemigroupsError: if ``u.throw_if_contains_unique_letter(w)`` throws for any ``w`` in *words*. .. seealso:: - + * :any:`ukkonen.add_word`; * :any:`Ukkonen.throw_if_contains_unique_letter`. @@ -258,7 +258,7 @@ represented by the :any:`Ukkonen` instance *u*. Find the length of the maximal prefix of a word occurring in two different places in a word in a suffix tree. Returns the length of the maximal length prefix of *w* that occurs in at least -:math:`2` different (possibly overlapping) places in the words contained in *u*. +:math:`2` different (possibly overlapping) places in the words contained in *u*. If no such prefix exists, then ``0`` is returned. :param u: the :any:`Ukkonen` object. @@ -535,7 +535,7 @@ the portion of *w* that was consumed in the traversal. For an implementation of Ukkonen's algorithm. This class implements Ukkonen's algorithm for constructing a generalised suffix -tree consisting of ``List[int]`` . The implementation in this class is based on: +tree consisting of ``List[int]``. The implementation in this class is based on: `https://cp-algorithms.com/string/suffix-tree-ukkonen.html `_ The suffix tree is updated when the member function :any:`ukkonen.add_word` is @@ -577,7 +577,7 @@ Default constructor.)pbdoc"); R"pbdoc( Construct from index and position. -:param vv: the index of the node reached. +:param vv: the index of the node reached. :type vv: int :param ppos: the position in the edge leading to vv. :type ppos: int @@ -629,10 +629,10 @@ The index of one past the last letter in the edge leading to the node. R"pbdoc( Construct a node from left most index, right most index, and parent. -:param l: the left most index and value of the data member l (defaults to ``0``). +:param l: the left most index and value of the data member l (defaults to ``0``). :type l: int -:param r: one after the right most index and value of the data member r (defaults to ``0``). +:param r: one after the right most index and value of the data member r (defaults to ``0``). :type r: int :param parent: parent of the node being constructed (defaults to :any:`UNDEFINED`). @@ -663,7 +663,7 @@ The index of the child node corresponding to a letter (if any). R"pbdoc( Returns ``True``` if the node is a leaf and ``False`` if not. -:returns: Whether the node is a leaf. +:returns: Whether the node is a leaf. :rtype: bool :complexity: Constant. diff --git a/src/word-graph.cpp b/src/word-graph.cpp index 6063d89b..e0200fec 100644 --- a/src/word-graph.cpp +++ b/src/word-graph.cpp @@ -97,7 +97,7 @@ Copy a :any:`WordGraph` object. Construct from number of nodes and out degree. This function constructs a word graph with *m* nodes and where the maximum -out-degree of any node is *n* . There are no edges in the defined word graph. +out-degree of any node is *n*. There are no edges in the defined word graph. :param m: the number of nodes in the word graph (default: ``0``). :type m: int @@ -597,7 +597,7 @@ Adds a cycle consisting of *N* new nodes. :sig=(wg: WordGraph) -> numpy.ndarray[numpy.float64[m, n]] | Matrix: Returns the adjacency matrix of a word graph. -This function returns the adjacency matrix of the word graph *wg* . The +This function returns the adjacency matrix of the word graph *wg*. The type of the returned matrix depends on whether or not ``libsemigroups`` is compiled with `eigen `_ enabled. The returned matrix has the number of edges with source ``s`` and target ``t`` in the @@ -1212,7 +1212,7 @@ Standardizes a word graph in-place. This function standardizes the word graph *wg* according to the reduction order specified by *val*, and replaces the contents of the :any:`Forest` *f* with a spanning tree rooted at ``0`` for the node reachable from -``0`` . The spanning tree corresponds to the order *val*. +``0``. The spanning tree corresponds to the order *val*. :param wg: the word graph. :type wg: Graph @@ -1241,7 +1241,7 @@ Standardizes a word graph in-place. This function standardizes the word graph *wg* according to the reduction order specified by *val*, and returns a :any:`Forest` object containing -a spanning tree rooted at ``0`` for the node reachable from ``0`` . The +a spanning tree rooted at ``0`` for the node reachable from ``0``. The spanning tree corresponds to the order *val*. :param wg: the word graph. diff --git a/src/words.cpp b/src/words.cpp index 17346b01..00e8ff47 100644 --- a/src/words.cpp +++ b/src/words.cpp @@ -432,7 +432,7 @@ Sets the order of the words in a :any:`WordRange` object to *val*. The possible size of the range. Returns the number of words in a :any:`WordRange` object if -:any:`WordRange.order()` is :any:`Order.shortlex` . If :any:`WordRange.order()` +:any:`WordRange.order()` is :any:`Order.shortlex`. If :any:`WordRange.order()` is not :any:`Order.shortlex`, then the return value of this function is meaningless. @@ -843,7 +843,7 @@ Sets the order of the strings in a :any:`StringRange` object to *val*. The possible size of the range. Returns the number of words in a :any:`StringRange` object if -:any:`StringRange.order()` is :any:`Order.shortlex` . If :any:`order()` is not +:any:`StringRange.order()` is :any:`Order.shortlex`. If :any:`order()` is not :any:`Order.shortlex` , then the return value of this function is meaningless. :returns: A value of type ``int``. @@ -1444,7 +1444,7 @@ Returns a product of letters. Let *elts* correspond to the ordered set :math:`a_0, a_1, \ldots, a_{n -1}` , *first* to :math:`f` , *last* to :math:`l` , and *step* to :math:`s`. If :math:`f \leq l` , let :math:`k` be the greatest positive integer such that -:math:`f + ks < l` . Then this function returns the word corresponding to +:math:`f + ks < l`. Then this function returns the word corresponding to :math:`a_f a_{f + s} a_{f + 2s} \cdots a_{f + ks}`. All subscripts are taken modulo :math:`n`. diff --git a/tests/old_tests/test_knuth_bendix.py b/tests/test_knuth_bendix.py similarity index 76% rename from tests/old_tests/test_knuth_bendix.py rename to tests/test_knuth_bendix.py index e2833443..232e2595 100644 --- a/tests/old_tests/test_knuth_bendix.py +++ b/tests/test_knuth_bendix.py @@ -23,7 +23,6 @@ presentation, LibsemigroupsError, POSITIVE_INFINITY, - overlap, is_obviously_infinite, ) @@ -53,30 +52,6 @@ # def test_validation(): # check_validation(KnuthBendix) -# TODO: Uncomment this if we keep validate_word functionality -# def test_validation(): -# ReportGuard(False) -# p = Presentation([0, 1]) -# kb = KnuthBendix(congruence_kind.twosided, p) - -# with pytest.raises(LibsemigroupsError): -# kb.validate_word([2]) -# try: -# kb.validate_word([0]) -# except LibsemigroupsError as e: -# pytest.fail( -# "unexpected exception raised for KnuthBendix::validate_word: " + e -# ) - -# with pytest.raises(LibsemigroupsError): -# kb.validate_word([0, 1, 2]) -# try: -# kb.validate_word([0, 1, 0, 1, 0, 1, 1, 1, 0]) -# except LibsemigroupsError as e: -# pytest.fail( -# "unexpected exception raised for KnuthBendix::validate_word: " + e -# ) - def check_initialisation(*args): for rewriter in ["RewriteFromLeft", "RewriteTrie"]: @@ -86,41 +61,31 @@ def check_initialisation(*args): def test_initialisation(): ReportGuard(False) - congs = [ - congruence_kind.twosided, - congruence_kind.left, - congruence_kind.right, - ] - for cong in congs: - check_initialisation(cong) + kinds = [congruence_kind.twosided, congruence_kind.onesided] p = Presentation("ba") presentation.add_rule(p, "ba", "ab") presentation.add_rule(p, "aa", "a") presentation.add_rule(p, "bb", "b") - for cong in congs: - check_initialisation(cong, p) + for kind in kinds: + check_initialisation(kind, p) p = Presentation([0, 1]) presentation.add_rule(p, [0, 1], [1, 0]) presentation.add_rule(p, [0, 0], [0]) presentation.add_rule(p, [1, 1], [1]) - for cong in congs: - check_initialisation(cong, p) + for kind in kinds: + check_initialisation(kind, p) - for cong in congs: - kb = KnuthBendix(cong, p) - kb2 = KnuthBendix(kb) + for kind in kinds: + kb = KnuthBendix(kind, p) + kb2 = kb.copy() kb2.run() with pytest.raises(TypeError): KnuthBendix(kb, rewriter="RewriteFromLeft") - kb = KnuthBendix(cong, p, rewriter="RewriteFromLeft") - kb2 = KnuthBendix(kb, rewriter="RewriteFromLeft") - - with pytest.raises(TypeError): - KnuthBendix(kb, rewriter="RewriteTrie") + kb = KnuthBendix(kind, p, rewriter="RewriteFromLeft") def test_attributes(): @@ -149,12 +114,11 @@ def test_attributes(): ("BaBa", "abab"), ] ) - assert kb.batch_size() == 128 + assert kb.max_pending_rules() == 128 assert kb.check_confluence_interval() == 4096 assert kb.max_overlap() == POSITIVE_INFINITY assert kb.max_rules() == POSITIVE_INFINITY - assert isinstance(kb.overlap_policy(), overlap) - assert kb.overlap_policy() == overlap.ABC + assert kb.overlap_policy() == kb.options.overlap.ABC assert kb.presentation().alphabet() == "abBe" assert kb.number_of_active_rules() == 12 assert kb.number_of_inactive_rules() == 0 @@ -170,24 +134,24 @@ def test_operators(): presentation.add_rule(p, "BaBa", "abab") kb = KnuthBendix(congruence_kind.twosided, p) - assert kb.equal_to("bb", "B") + assert kb.contains("bb", "B") # REVIEW Should this be allowed - # assert kb.equal_to([1, 1], [2]) + # assert kb.contains([1, 1], [2]) with pytest.raises(LibsemigroupsError): - kb.equal_to("aa", "z") + kb.contains("aa", "z") - assert kb.normal_form("bb") == "B" - assert kb.normal_form("B") == "B" + assert kb.reduce_no_run("bb") == "B" + assert kb.reduce_no_run("B") == "B" with pytest.raises(LibsemigroupsError): - kb.normal_form("z") + kb.reduce_no_run("z") - assert kb.rewrite("aa") == "e" - assert kb.rewrite("bb") == "B" + assert kb.reduce_no_run("aa") == "e" + assert kb.reduce_no_run("bb") == "B" with pytest.raises(LibsemigroupsError): - kb.rewrite("z") + kb.reduce_no_run("z") def test_running_state(): @@ -250,9 +214,9 @@ def test_003(): assert not kb.confluent() kb.run() assert kb.confluent() - assert kb.equal_to("Aba", "bb") - assert kb.equal_to("Bcb", "cc") - assert kb.equal_to("Cac", "aa") + assert kb.contains("Aba", "bb") + assert kb.contains("Bcb", "cc") + assert kb.contains("Cac", "aa") def test_003_b(): @@ -267,9 +231,9 @@ def test_003_b(): assert not kb.confluent() kb.run() assert kb.confluent() - assert kb.equal_to("Aba", "bb") - assert kb.equal_to("Bcb", "cc") - assert kb.equal_to("Cac", "aa") + assert kb.contains("Aba", "bb") + assert kb.contains("Bcb", "cc") + assert kb.contains("Cac", "aa") def test_004(): @@ -286,17 +250,17 @@ def test_004(): kb.run() assert kb.confluent() assert kb.number_of_active_rules() == 183 - assert kb.equal_to("aaa", "") - assert kb.equal_to("bbb", "") - assert kb.equal_to("BaBaBaBaB", "aa") - assert kb.equal_to("bababa", "aabb") - assert kb.equal_to("ababab", "bbaa") - assert kb.equal_to("aabbaa", "babab") - assert kb.equal_to("bbaabb", "ababa") - assert kb.equal_to("bababbabab", "aabbabbaa") - assert kb.equal_to("ababaababa", "bbaabaabb") - assert kb.equal_to("bababbabaababa", "aabbabbaabaabb") - assert kb.equal_to("bbaabaabbabbaa", "ababaababbabab") + assert kb.contains("aaa", "") + assert kb.contains("bbb", "") + assert kb.contains("BaBaBaBaB", "aa") + assert kb.contains("bababa", "aabb") + assert kb.contains("ababab", "bbaa") + assert kb.contains("aabbaa", "babab") + assert kb.contains("bbaabb", "ababa") + assert kb.contains("bababbabab", "aabbabbaa") + assert kb.contains("ababaababa", "bbaabaabb") + assert kb.contains("bababbabaababa", "aabbabbaabaabb") + assert kb.contains("bbaabaabbabbaa", "ababaababbabab") def test_004_b(): @@ -314,20 +278,19 @@ def test_004_b(): kb.run() assert kb.confluent() assert kb.number_of_active_rules() == 184 # Was 183. 1 more for ee=e - assert kb.equal_to("aaa", "") - assert kb.equal_to("bbb", "") - assert kb.equal_to("BaBaBaBaB", "aa") - assert kb.equal_to("bababa", "aabb") - assert kb.equal_to("ababab", "bbaa") - assert kb.equal_to("aabbaa", "babab") - assert kb.equal_to("bbaabb", "ababa") - assert kb.equal_to("bababbabab", "aabbabbaa") - assert kb.equal_to("ababaababa", "bbaabaabb") - assert kb.equal_to("bababbabaababa", "aabbabbaabaabb") - assert kb.equal_to("bbaabaabbabbaa", "ababaababbabab") - - -# TODO Add this back when obviously infinite has been implemented + assert kb.contains("aaa", "") + assert kb.contains("bbb", "") + assert kb.contains("BaBaBaBaB", "aa") + assert kb.contains("bababa", "aabb") + assert kb.contains("ababab", "bbaa") + assert kb.contains("aabbaa", "babab") + assert kb.contains("bbaabb", "ababa") + assert kb.contains("bababbabab", "aabbabbaa") + assert kb.contains("ababaababa", "bbaabaabb") + assert kb.contains("bababbabaababa", "aabbabbaabaabb") + assert kb.contains("bbaabaabbabbaa", "ababaababbabab") + + def test_005(): p = Presentation("aAbB") p.contains_empty_word(True) @@ -507,9 +470,9 @@ def test_006(): # assert kk.identity() == "\x80" # k.set_inverses(k.alphabet()) # assert k.inverses() == k.alphabet() -# assert k.equal_to("\x80", "a") -# assert k.normal_form("\x80") == "\x80" -# assert k.normal_form("a") == "\x80" +# assert k.contains("\x80", "a") +# assert k.reduce_no_run("\x80") == "\x80" +# assert k.reduce_no_run("a") == "\x80" # k.validate_letter("\x80") # k.validate_word("\x80") # assert k.char_to_uint("\x80") == 127 diff --git a/tests/test_todd_coxeter.py b/tests/test_todd_coxeter.py index ea4f6609..9a452d27 100644 --- a/tests/test_todd_coxeter.py +++ b/tests/test_todd_coxeter.py @@ -12,6 +12,7 @@ """ from datetime import timedelta +from typing import List import pytest @@ -75,7 +76,7 @@ def test_attributes(): assert not tc.contains([0, 0, 0], [0, 0]) assert tc.currently_contains([0, 0, 0], [0, 0]) == tril.false assert tc.kind() == congruence_kind.onesided - assert tc.word_of(1) == [0, 0] + assert tc.word_of(1, Word=List[int]) == [0, 0] assert tc.index_of([0, 0]) == 1 assert tc.number_of_generating_pairs() == 0 assert tc.generating_pairs() == [] @@ -186,7 +187,7 @@ def test_000_iterators(): [1, 0], ] - assert list(todd_coxeter.word_normal_forms(tc)) == [ + assert list(todd_coxeter.normal_forms(tc, Word=List[int])) == [ [0], [1], [0, 0], @@ -284,7 +285,7 @@ def test_096(): assert word_graph.is_compatible(wg, 0, wg.number_of_nodes(), lhs, rhs) assert tc.number_of_classes() == 1 tc.shrink_to_fit() - assert list(todd_coxeter.word_normal_forms(tc)) == [[0]] + assert list(todd_coxeter.normal_forms(tc, Word=List[int])) == [[0]] assert word_graph.is_complete(tc.current_word_graph()) for lhs, rhs in ((p.rules[i], p.rules[i + 1]) for i in range(0, len(p.rules), 2)): assert word_graph.is_compatible(wg, 0, wg.number_of_nodes(), lhs, rhs)