diff --git a/docs/source/data-structures/order/index.rst b/docs/source/data-structures/order/index.rst index aeecf638..30a0a68e 100644 --- a/docs/source/data-structures/order/index.rst +++ b/docs/source/data-structures/order/index.rst @@ -32,9 +32,16 @@ Contents .. autosummary:: :signatures: short + len_wt_lex_cmp lenlex_cmp lex_cmp + rev_len_wt_lex_cmp + rev_lenlex_cmp + rev_lex_cmp rev_rpo_cmp + rev_wr_cmp + rev_wt_lenlex_cmp + rev_wt_lex_cmp rpo_cmp wr_cmp wt_lenlex_cmp @@ -43,12 +50,26 @@ Contents Full API -------- +.. autofunction:: len_wt_lex_cmp + .. autofunction:: lenlex_cmp .. autofunction:: lex_cmp +.. autofunction:: rev_len_wt_lex_cmp + +.. autofunction:: rev_lenlex_cmp + +.. autofunction:: rev_lex_cmp + .. autofunction:: rev_rpo_cmp +.. autofunction:: rev_wr_cmp + +.. autofunction:: rev_wt_lenlex_cmp + +.. autofunction:: rev_wt_lex_cmp + .. autofunction:: rpo_cmp .. autofunction:: wr_cmp diff --git a/src/libsemigroups_pybind11/__init__.py b/src/libsemigroups_pybind11/__init__.py index e6128171..212f2498 100644 --- a/src/libsemigroups_pybind11/__init__.py +++ b/src/libsemigroups_pybind11/__init__.py @@ -107,6 +107,7 @@ delta, error_message_with_prefix, freeband_equal_to, + len_wt_lex_cmp as _len_wt_lex_cmp, lenlex_cmp as _lenlex_cmp, lex_cmp as _lex_cmp, lexicographical_compare, @@ -115,7 +116,13 @@ random_strings, random_word, recursive_path_compare, + rev_len_wt_lex_cmp as _rev_len_wt_lex_cmp, + rev_lenlex_cmp as _rev_lenlex_cmp, + rev_lex_cmp as _rev_lex_cmp, rev_rpo_cmp as _rev_rpo_cmp, + rev_wr_cmp as _rev_wr_cmp, + rev_wt_lenlex_cmp as _rev_wt_lenlex_cmp, + rev_wt_lex_cmp as _rev_wt_lex_cmp, rpo_cmp as _rpo_cmp, shortlex_compare, side, @@ -130,9 +137,16 @@ ) from e +len_wt_lex_cmp = _wrap_cxx_free_fn(_len_wt_lex_cmp) lenlex_cmp = _wrap_cxx_free_fn(_lenlex_cmp) lex_cmp = _wrap_cxx_free_fn(_lex_cmp) +rev_len_wt_lex_cmp = _wrap_cxx_free_fn(_rev_len_wt_lex_cmp) +rev_lenlex_cmp = _wrap_cxx_free_fn(_rev_lenlex_cmp) +rev_lex_cmp = _wrap_cxx_free_fn(_rev_lex_cmp) rev_rpo_cmp = _wrap_cxx_free_fn(_rev_rpo_cmp) +rev_wr_cmp = _wrap_cxx_free_fn(_rev_wr_cmp) +rev_wt_lenlex_cmp = _wrap_cxx_free_fn(_rev_wt_lenlex_cmp) +rev_wt_lex_cmp = _wrap_cxx_free_fn(_rev_wt_lex_cmp) rpo_cmp = _wrap_cxx_free_fn(_rpo_cmp) wt_lenlex_cmp = _wrap_cxx_free_fn(_wt_lenlex_cmp) wt_lex_cmp = _wrap_cxx_free_fn(_wt_lex_cmp) @@ -176,6 +190,7 @@ "delta", "error_message_with_prefix", "freeband_equal_to", + "len_wt_lex_cmp", "lenlex_cmp", "lex_cmp", "lexicographical_compare", @@ -184,7 +199,13 @@ "random_strings", "random_word", "recursive_path_compare", + "rev_len_wt_lex_cmp", + "rev_lenlex_cmp", + "rev_lex_cmp", "rev_rpo_cmp", + "rev_wr_cmp", + "rev_wt_lenlex_cmp", + "rev_wt_lex_cmp", "rpo_cmp", "shortlex_compare", "side", diff --git a/src/order.cpp b/src/order.cpp index 0e9a46fa..54c6b99c 100644 --- a/src/order.cpp +++ b/src/order.cpp @@ -131,7 +131,7 @@ Letters are compared by their positions in *alphabet*. R"pbdoc( :sig=(x: str | list[int], y: str | list[int]) -> bool: :only-document-once: -Compare two words using len-lex ordering. +Compare two words using lenlex ordering. Words are first ordered by length and then lexicographically. @@ -162,7 +162,7 @@ Words are first ordered by length and then lexicographically. R"pbdoc( :sig=(alphabet: Alphabet, x: str | list[int], y: str | list[int]) -> bool: :only-document-once: -Compare two words using len-lex ordering and an alphabet. +Compare two words using lenlex ordering and an alphabet. Words are first ordered by length and then lexicographically, with letters compared by their positions in *alphabet*. @@ -180,13 +180,668 @@ compared by their positions in *alphabet*. belong to *alphabet*. .. doctest:: python - >>> from libsemigroups_pybind11 import Alphabet, lenlex_cmp >>> alphabet = Alphabet("ba") >>> lenlex_cmp(alphabet, "b", "a") True )pbdoc"); + m.def( + "rev_lex_cmp", + [](Word const& x, Word const& y) { return rev_lex_cmp(x, y); }, + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using reversed lexicographic ordering. + +The words are read from right to left when being compared. + +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +.. doctest:: python + + >>> from libsemigroups_pybind11 import rev_lex_cmp + >>> rev_lex_cmp("ba", "ab") + True +)pbdoc"); + + m.def( + "rev_lex_cmp", + [](Alphabet const& alphabet, Word const& x, Word const& y) { + return rev_lex_cmp(alphabet, x, y); + }, + py::arg("alphabet"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(alphabet: Alphabet, x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using alphabet-aware reversed lexicographic ordering. + +Letters are compared by their positions in *alphabet*, and the words are read +from right to left when being compared. + +:param alphabet: the ordered alphabet containing the letters of both words. +:type alphabet: Alphabet +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if either word contains a letter that does not + belong to *alphabet*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, rev_lex_cmp + >>> rev_lex_cmp(Alphabet("ab"), "ba", "ab") + True +)pbdoc"); + + m.def( + "rev_lenlex_cmp", + [](Word const& x, Word const& y) { return rev_lenlex_cmp(x, y); }, + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using reversed lenlex ordering. + +Words are first ordered by length and then lexicographically reading +from right to left. + +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +.. doctest:: python + + >>> from libsemigroups_pybind11 import rev_lenlex_cmp + >>> rev_lenlex_cmp([1, 0], [0, 1]) + True +)pbdoc"); + + m.def( + "rev_lenlex_cmp", + [](Alphabet const& alphabet, Word const& x, Word const& y) { + return rev_lenlex_cmp(alphabet, x, y); + }, + py::arg("alphabet"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(alphabet: Alphabet, x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using alphabet-aware reversed lenlex ordering. + +Words are first ordered by length and then lexicographically reading +from right to left. Letters are compared by their positions in *alphabet*. + +:param alphabet: the ordered alphabet containing the letters of both words. +:type alphabet: Alphabet +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if either word contains a letter that does not + belong to *alphabet*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, rev_lenlex_cmp + >>> rev_lenlex_cmp(Alphabet("ab"), "ba", "ab") + True +)pbdoc"); + + m.def( + "wt_lenlex_cmp", + [](std::vector const& weights, Word const& x, Word const& y) { + return wt_lenlex_cmp(weights, x, y); + }, + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using weighted lenlex ordering. + +The *i*-th entry of *weights* is the weight assigned to generator *i*. +Words are first ordered by their total weight, then by length, and finally +lexicographically. + +:param weights: the weight assigned to each generator. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import wt_lenlex_cmp + >>> wt_lenlex_cmp([1, 2], [1], [0, 0]) + True +)pbdoc"); + + m.def( + "wt_lenlex_cmp", + [](Alphabet const& alphabet, + std::vector const& weights, + Word const& x, + Word const& y) { return wt_lenlex_cmp(alphabet, weights, x, y); }, + py::arg("alphabet"), + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(alphabet: Alphabet, weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using alphabet-aware weighted lenlex ordering. + +Letters are mapped to their positions in *alphabet*, and the *i*-th entry of +*weights* is the weight assigned to the *i*-th letter of *alphabet*. Words are +first ordered by their total weight, then by length, and finally +lexicographically according to *alphabet*. + +:param alphabet: the ordered alphabet containing the letters of both words. +:type alphabet: Alphabet +:param weights: the weight assigned to each letter of *alphabet*. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter does not belong to *alphabet*, or its + position in *alphabet* is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, wt_lenlex_cmp + >>> wt_lenlex_cmp(Alphabet("ba"), [1, 1], "b", "a") + True +)pbdoc"); + + m.def( + "wt_lex_cmp", + [](std::vector const& weights, Word const& x, Word const& y) { + return wt_lex_cmp(weights, x, y); + }, + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using weighted lexicographic ordering. + +The *i*-th entry of *weights* is the weight assigned to generator *i*. +Words are first ordered by their total weight and then lexicographically. + +:param weights: the weight assigned to each generator. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import wt_lex_cmp + >>> wt_lex_cmp([1, 2], [1], [0, 0]) + False +)pbdoc"); + + m.def( + "wt_lex_cmp", + [](Alphabet const& alphabet, + std::vector const& weights, + Word const& x, + Word const& y) { return wt_lex_cmp(alphabet, weights, x, y); }, + py::arg("alphabet"), + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(alphabet: Alphabet, weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using alphabet-aware weighted lexicographic ordering. + +Letters are mapped to their positions in *alphabet*, and the *i*-th entry of +*weights* is the weight assigned to the *i*-th letter of *alphabet*. Words are +first ordered by their total weight and then lexicographically according to +*alphabet*. + +:param alphabet: the ordered alphabet containing the letters of both words. +:type alphabet: Alphabet +:param weights: the weight assigned to each letter of *alphabet*. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter does not belong to *alphabet*, or its + position in *alphabet* is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, wt_lex_cmp + >>> wt_lex_cmp(Alphabet("ba"), [1, 1], "b", "a") + True +)pbdoc"); + + m.def( + "len_wt_lex_cmp", + [](std::vector const& weights, Word const& x, Word const& y) { + return len_wt_lex_cmp(weights, x, y); + }, + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words by length, weight, and lexicographic order. + +Words are first ordered by length, then by total weight, and finally +lexicographically. The *i*-th entry of *weights* is the weight assigned to +generator *i*. + +:param weights: the weight assigned to each generator. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import len_wt_lex_cmp + >>> len_wt_lex_cmp([1, 1], [0, 1], [1, 0]) + True +)pbdoc"); + + m.def( + "len_wt_lex_cmp", + [](Alphabet const& alphabet, + std::vector const& weights, + Word const& x, + Word const& y) { return len_wt_lex_cmp(alphabet, weights, x, y); }, + py::arg("alphabet"), + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(alphabet: Alphabet, weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words by length, weight, and alphabet-aware lexicographic order. + +Letters are mapped to their positions in *alphabet*, and the *i*-th entry of +*weights* is the weight assigned to the *i*-th letter of *alphabet*. Words are +first ordered by length, then by total weight, and finally lexicographically +according to *alphabet*. + +:param alphabet: the ordered alphabet containing the letters of both words. +:type alphabet: Alphabet +:param weights: the weight assigned to each letter of *alphabet*. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter does not belong to *alphabet*, or its + position in *alphabet* is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, len_wt_lex_cmp + >>> len_wt_lex_cmp(Alphabet("ab"), [1, 1], "ab", "ba") + True +)pbdoc"); + + m.def( + "rev_wr_cmp", + [](std::vector const& levels, Word const& x, Word const& y) { + return rev_wr_cmp(levels, x, y); + }, + py::arg("levels"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(levels: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using reversed wreath-product ordering. + +The words are read from right to left when being compared. The *i*-th entry +of *levels* is the wreath-product level assigned to generator *i*. + +:param levels: the level assigned to each generator. +:type levels: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter is not a valid index into *levels*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import rev_wr_cmp + >>> rev_wr_cmp([0, 0], [1, 0], [0, 1]) + True +)pbdoc"); + + m.def( + "rev_wr_cmp", + [](Alphabet const& alphabet, + std::vector const& levels, + Word const& x, + Word const& y) { return rev_wr_cmp(alphabet, levels, x, y); }, + py::arg("alphabet"), + py::arg("levels"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(alphabet: Alphabet, levels: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using alphabet-aware reversed wreath-product ordering. + +Letters are mapped to their positions in *alphabet*, and the *i*-th entry of +*levels* is the wreath-product level assigned to the *i*-th letter of +*alphabet*. The words are read from right to left when being compared. + +:param alphabet: the ordered alphabet containing the letters of both words. +:type alphabet: Alphabet +:param levels: the level assigned to each letter of *alphabet*. +:type levels: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter does not belong to *alphabet*, or its + position in *alphabet* is not a valid index into *levels*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, rev_wr_cmp + >>> rev_wr_cmp(Alphabet("ab"), [0, 0], "ba", "ab") + True +)pbdoc"); + + m.def( + "rev_wt_lenlex_cmp", + [](std::vector const& weights, Word const& x, Word const& y) { + return rev_wt_lenlex_cmp(weights, x, y); + }, + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using reversed weighted lenlex ordering. + +Words are first ordered by total weight, then by length, and finally +lexicographically read from right to left. The *i*-th entry of +*weights* is the weight assigned to generator *i*. + +:param weights: the weight assigned to each generator. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import rev_wt_lenlex_cmp + >>> rev_wt_lenlex_cmp([1, 1], [1, 0], [0, 1]) + True +)pbdoc"); + + m.def( + "rev_wt_lenlex_cmp", + [](Alphabet const& alphabet, + std::vector const& weights, + Word const& x, + Word const& y) { + return rev_wt_lenlex_cmp(alphabet, weights, x, y); + }, + py::arg("alphabet"), + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(alphabet: Alphabet, weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using alphabet-aware reversed weighted lenlex ordering. + +Letters are mapped to their positions in *alphabet*, and the *i*-th entry of +*weights* is the weight assigned to the *i*-th letter of *alphabet*. Words are +first ordered by total weight, then by length, and finally lexicographically +read from right to left. + +:param alphabet: the ordered alphabet containing the letters of both words. +:type alphabet: Alphabet +:param weights: the weight assigned to each letter of *alphabet*. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter does not belong to *alphabet*, or its + position in *alphabet* is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, rev_wt_lenlex_cmp + >>> rev_wt_lenlex_cmp(Alphabet("ab"), [1, 1], "ba", "ab") + True +)pbdoc"); + + m.def( + "rev_wt_lex_cmp", + [](std::vector const& weights, Word const& x, Word const& y) { + return rev_wt_lex_cmp(weights, x, y); + }, + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using reversed weighted lexicographic ordering. + +Words are first ordered by total weight and then lexicographically +read from right to left. The *i*-th entry of *weights* is the weight assigned +to generator *i*. + +:param weights: the weight assigned to each generator. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import rev_wt_lex_cmp + >>> rev_wt_lex_cmp([1, 1], [1, 0], [0, 1]) + True +)pbdoc"); + + m.def( + "rev_wt_lex_cmp", + [](Alphabet const& alphabet, + std::vector const& weights, + Word const& x, + Word const& y) { return rev_wt_lex_cmp(alphabet, weights, x, y); }, + py::arg("alphabet"), + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(alphabet: Alphabet, weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words using alphabet-aware reversed weighted lexicographic ordering. + +Letters are mapped to their positions in *alphabet*, and the *i*-th entry of +*weights* is the weight assigned to the *i*-th letter of *alphabet*. Words are +first ordered by total weight and then lexicographically read from +right to left. + +:param alphabet: the ordered alphabet containing the letters of both words. +:type alphabet: Alphabet +:param weights: the weight assigned to each letter of *alphabet*. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter does not belong to *alphabet*, or its + position in *alphabet* is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, rev_wt_lex_cmp + >>> rev_wt_lex_cmp(Alphabet("ab"), [1, 1], "ba", "ab") + True +)pbdoc"); + + m.def( + "rev_len_wt_lex_cmp", + [](std::vector const& weights, Word const& x, Word const& y) { + return rev_len_wt_lex_cmp(weights, x, y); + }, + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words by length, weight, and reversed lexicographic order. + +Words are first ordered by length, then by total weight, and finally +lexicographically read from right to left. The *i*-th entry of +*weights* is the weight assigned to generator *i*. + +:param weights: the weight assigned to each generator. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import rev_len_wt_lex_cmp + >>> rev_len_wt_lex_cmp([1, 1], [1, 0], [0, 1]) + True +)pbdoc"); + + m.def( + "rev_len_wt_lex_cmp", + [](Alphabet const& alphabet, + std::vector const& weights, + Word const& x, + Word const& y) { + return rev_len_wt_lex_cmp(alphabet, weights, x, y); + }, + py::arg("alphabet"), + py::arg("weights"), + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(alphabet: Alphabet, weights: list[int], x: str | list[int], y: str | list[int]) -> bool: +:only-document-once: +Compare two words by length, weight, and alphabet-aware reversed lexicographic order. + +Letters are mapped to their positions in *alphabet*, and the *i*-th entry of +*weights* is the weight assigned to the *i*-th letter of *alphabet*. Words are +first ordered by length, then by total weight, and finally lexicographically +read from right to left. + +:param alphabet: the ordered alphabet containing the letters of both words. +:type alphabet: Alphabet +:param weights: the weight assigned to each letter of *alphabet*. +:type weights: list[int] +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises LibsemigroupsError: if a letter does not belong to *alphabet*, or its + position in *alphabet* is not a valid index into *weights*. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, rev_len_wt_lex_cmp + >>> rev_len_wt_lex_cmp(Alphabet("ab"), [1, 1], "ba", "ab") + True +)pbdoc"); + m.def( "rpo_cmp", [](Word const& x, Word const& y) { return rpo_cmp(x, y); }, @@ -338,7 +993,7 @@ left, with letters compared by their positions in *alphabet*. R"pbdoc( :sig=(weights: list[int], x: str | list[int], y: str | list[int]) -> bool: :only-document-once: -Compare two words using weighted len-lex ordering. +Compare two words using weighted lenlex ordering. The *i*-th entry of *weights* is the weight assigned to generator *i*. Words are first ordered by their total weight, then by length, and finally @@ -375,7 +1030,7 @@ lexicographically. R"pbdoc( :sig=(alphabet: Alphabet, weights: list[int], x: str | list[int], y: str | list[int]) -> bool: :only-document-once: -Compare two words using alphabet-aware weighted len-lex ordering. +Compare two words using alphabet-aware weighted lenlex ordering. Letters are mapped to their positions in *alphabet*, and the *i*-th entry of *weights* is the weight assigned to the *i*-th letter of *alphabet*. Words are @@ -511,7 +1166,7 @@ Compare two words using wreath-product ordering. The *i*-th entry of *levels* is the level assigned to generator *i*. Differences at higher levels dominate differences at lower levels, and -differences within one level are compared using len-lex ordering. +differences within one level are compared using lenlex ordering. :param levels: the level assigned to each generator. :type levels: list[int] @@ -550,7 +1205,7 @@ Compare two words using alphabet-aware wreath-product ordering. Letters are mapped to their positions in *alphabet*, and the *i*-th entry of *levels* is the level assigned to the *i*-th letter of *alphabet*. Differences at higher levels dominate differences at lower levels, and -differences within one level are compared using len-lex ordering. +differences within one level are compared using lenlex ordering. :param alphabet: the ordered alphabet containing the letters of both words. :type alphabet: Alphabet @@ -600,7 +1255,7 @@ respectively, in new code. .. py:attribute:: Order.lenlex :value: - The len-lex ordering. Words are first ordered by length, and then + The lenlex ordering. Words are first ordered by length, and then lexicographically. .. py:attribute:: Order.shortlex diff --git a/src/present.cpp b/src/present.cpp index 727fe07a..5e5c34fb 100644 --- a/src/present.cpp +++ b/src/present.cpp @@ -946,10 +946,10 @@ Adds rules to *p* of the form :math:`uv = vu` for every letter :math:`u` in R"pbdoc( :sig=(p: Presentation) -> bool: :only-document-once: -Check the rules are sorted relative to len-lex. +Check the rules are sorted relative to lenlex. Check if the rules :math:`u_1 = v_1, \ldots, u_n = v_n` satisfy -:math:`u_1v_1 < \cdots < u_nv_n` where :math:`<` is len-lex order. +:math:`u_1v_1 < \cdots < u_nv_n` where :math:`<` is lenlex order. :param p: the presentation to check. :type p: Presentation @@ -1496,9 +1496,9 @@ is defined to be the sum of the lengths of its left-hand and right-hand sides. R"pbdoc( :sig=(p: Presentation) -> bool: :only-document-once: -Sort the left-hand and right-hand side of each rule by len-lex. +Sort the left-hand and right-hand side of each rule by lenlex. -Sort each rule :math:`u = v` so that the left-hand side is len-lex greater than +Sort each rule :math:`u = v` so that the left-hand side is lenlex greater than the right-hand side, and return :any:`True` if any of the rules are changed. :param p: the presentation whose rules should be sorted. @@ -1548,10 +1548,10 @@ rules are changed. R"pbdoc( :sig=(p: Presentation) -> None: :only-document-once: -Sort all of the rules by len-lex. +Sort all of the rules by lenlex. Sort the rules :math:`u_1 = v_1, \ldots, u_n = v_n` so that :math:`u_1v_1 < -\cdots < u_nv_n` where :math:`<` is the len-lex order. +\cdots < u_nv_n` where :math:`<` is the lenlex order. :param p: the presentation to sort. :type p: Presentation diff --git a/src/todd-coxeter.cpp b/src/todd-coxeter.cpp index 14a4ed54..5a852332 100644 --- a/src/todd-coxeter.cpp +++ b/src/todd-coxeter.cpp @@ -533,7 +533,7 @@ This function performs a lookbehind using the function *collapser* to decide whether or not to collapse nodes. For example, it might be the case that *collapser* uses a :any:`KnuthBendix` instance to determine whether or not nodes in the graph represent the same class of the congruence. More -specifically, the len-lex least path from the initial node to every node +specifically, the lenlex least path from the initial node to every node ``n`` is rewritten using *collapser*, and if the rewritten word labels a path in the graph to a node ``m``, then it is assumed that ``m`` and ``n`` represent the same class of the congruence, and they are marked for diff --git a/tests/test_order.py b/tests/test_order.py index a008ee7e..1b2f1950 100644 --- a/tests/test_order.py +++ b/tests/test_order.py @@ -12,11 +12,18 @@ Alphabet, LibsemigroupsError, Order, + len_wt_lex_cmp, lenlex_cmp, lex_cmp, lexicographical_compare, recursive_path_compare, + rev_len_wt_lex_cmp, + rev_lenlex_cmp, + rev_lex_cmp, rev_rpo_cmp, + rev_wr_cmp, + rev_wt_lenlex_cmp, + rev_wt_lex_cmp, rpo_cmp, shortlex_compare, wr_cmp, @@ -32,6 +39,10 @@ (lex_cmp, [0, 1], [1, 0]), (lenlex_cmp, "ba", "aaa"), (lenlex_cmp, [1, 0], [0, 0, 0]), + (rev_lex_cmp, "ab", "ba"), + (rev_lex_cmp, [0, 1], [1, 0]), + (rev_lenlex_cmp, "ba", "aaa"), + (rev_lenlex_cmp, [1, 0], [0, 0, 0]), (rpo_cmp, "ab", "ba"), (rpo_cmp, [0, 1], [1, 0]), (rev_rpo_cmp, "ab", "ba"), @@ -43,7 +54,9 @@ def test_compare_without_alphabet(compare, x, y): assert isinstance(compare(x, y), bool) -@pytest.mark.parametrize("compare", [lex_cmp, lenlex_cmp, rpo_cmp, rev_rpo_cmp]) +@pytest.mark.parametrize( + "compare", [lex_cmp, lenlex_cmp, rev_lex_cmp, rev_lenlex_cmp, rpo_cmp, rev_rpo_cmp] +) @pytest.mark.parametrize( ("alphabet", "x", "y", "missing"), [(Alphabet("ba"), "ba", "ab", "c"), (Alphabet([1, 0]), [1, 0], [0, 1], [2])], @@ -148,3 +161,53 @@ def test_weighted_comparisons_with_alphabet(compare): with pytest.raises(LibsemigroupsError): compare(alphabet, [1], "a", "b") + + +@pytest.mark.parametrize( + ("compare", "parameters"), + [ + (rev_wr_cmp, [0, 0]), + (rev_wt_lenlex_cmp, [1, 1]), + (rev_wt_lex_cmp, [1, 1]), + (rev_len_wt_lex_cmp, [1, 1]), + ], +) +def test_parameterized_reverse_comparisons(compare, parameters): + """Check parameterized reverse orders for both supported word types.""" + assert compare(parameters, [1, 0], [0, 1]) + assert compare(Alphabet("ab"), parameters, "ba", "ab") + + with pytest.raises(LibsemigroupsError): + compare(parameters, [2], [0, 1]) + + with pytest.raises(LibsemigroupsError): + compare(Alphabet("ab"), parameters, "c", "ab") + + +def test_reverse_weighted_comparisons_use_the_expected_priorities(): + """Check the weight and length priorities of the reverse weighted orders.""" + weights = [1, 2] + + assert rev_wt_lenlex_cmp(weights, [1], [0, 0]) + assert not rev_wt_lex_cmp(weights, [1], [0, 0]) + + weights = [100, 1] + assert rev_len_wt_lex_cmp(weights, [0], [1, 1]) + assert not rev_wt_lex_cmp(weights, [0], [1, 1]) + + +def test_len_wt_lex_cmp(): + """Check length-before-weighted-lex ordering and validation.""" + weights = [1, 1] + + assert len_wt_lex_cmp(weights, [0, 1], [1, 0]) + assert len_wt_lex_cmp(Alphabet("ab"), weights, "ab", "ba") + + with pytest.raises(LibsemigroupsError): + len_wt_lex_cmp(weights, [2], [0, 1]) + + with pytest.raises(LibsemigroupsError): + len_wt_lex_cmp(Alphabet("ab"), weights, "c", "ab") + + weights = [100, 1] + assert len_wt_lex_cmp(weights, [0], [1, 1])