Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/source/data-structures/presentations/present-helpers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Contents
.. autosummary::
:signatures: short

add_commutator_rule
add_cyclic_conjugates
add_identity_rules
add_inverse_rules
Expand All @@ -43,6 +44,7 @@ Contents
are_rules_sorted
balance
change_alphabet
commutator
contains_rule
first_unused_letter
greedy_reduce_length
Expand Down
7 changes: 5 additions & 2 deletions etc/check-params.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from glob import iglob

from bs4 import BeautifulSoup
from bs4.element import Tag

BOLD_TEXT = "\033[1m"
YELLOW = "\033[93m"
Expand All @@ -20,7 +21,7 @@ def warn(message):
print(YELLOW + f"WARNING: {message}" + END_COLOUR)


def extract_signature(func, func_name) -> tuple[dict[str, str], str]:
def extract_signature(func: Tag, func_name: str) -> tuple[dict[str, str], str]:
"""Extract the parameters and typehints from the signature of a function

This function interrogates the signature of a function and returns:
Expand All @@ -31,6 +32,8 @@ def extract_signature(func, func_name) -> tuple[dict[str, str], str]:
return_typehint = ""
sig = func.find("dt", class_="sig sig-object py")
for param in sig.find_all("em", class_="sig-param"):
if param.find("span", class_="keyword-only-separator"):
continue
param_component = param.find_all("span", class_="n")
if len(param_component) == 0 or len(param_component) > 2:
warn(f"unexpected element in doc of {func_name}. Skipping . . .")
Expand All @@ -44,7 +47,7 @@ def extract_signature(func, func_name) -> tuple[dict[str, str], str]:
return param_to_typehint, return_typehint


def extract_documented_signature(func, name) -> tuple[dict[str, str], str]:
def extract_documented_signature(func: Tag, name: str) -> tuple[dict[str, str], str]:
"""Extract the parameters and typehints from the docstring of a function

This function interrogates the docstring of a function and returns:
Expand Down
8 changes: 6 additions & 2 deletions src/libsemigroups_pybind11/detail/cxx_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,12 @@ def wrap_cxx_free_fn(cxx_free_fn: Pybind11Type) -> Callable:
returned function.
"""

def cxx_free_fn_wrapper(*args):
return to_py(cxx_free_fn(*(to_cxx(x) for x in args)))
def cxx_free_fn_wrapper(*args, **kwargs):
return to_py(
cxx_free_fn(
*(to_cxx(x) for x in args), **{key: to_cxx(value) for key, value in kwargs.items()}
)
)

update_wrapper(cxx_free_fn_wrapper, cxx_free_fn)
return cxx_free_fn_wrapper
Expand Down
4 changes: 4 additions & 0 deletions src/libsemigroups_pybind11/presentation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
InversePresentationWord as _InversePresentationWord,
PresentationString as _PresentationString,
PresentationWord as _PresentationWord,
presentation_add_commutator_rule as _add_commutator_rule,
presentation_add_cyclic_conjugates as _add_cyclic_conjugates,
presentation_add_identity_rules as _add_identity_rules,
presentation_add_inverse_rules as _add_inverse_rules,
Expand All @@ -22,6 +23,7 @@
presentation_are_rules_sorted as _are_rules_sorted,
presentation_balance as _balance,
presentation_change_alphabet as _change_alphabet,
presentation_commutator as _commutator,
presentation_contains_rule as _contains_rule,
presentation_first_unused_letter as _first_unused_letter,
presentation_greedy_reduce_length as _greedy_reduce_length,
Expand Down Expand Up @@ -227,13 +229,15 @@ def __init__(self: _Self, *args, **kwargs) -> None:
########################################################################


add_commutator_rule = _wrap_cxx_free_fn(_add_commutator_rule)
add_identity_rules = _wrap_cxx_free_fn(_add_identity_rules)
add_inverse_rules = _wrap_cxx_free_fn(_add_inverse_rules)
add_rule = _wrap_cxx_free_fn(_add_rule)
add_rules = _wrap_cxx_free_fn(_add_rules)
add_zero_rules = _wrap_cxx_free_fn(_add_zero_rules)
are_rules_sorted = _wrap_cxx_free_fn(_are_rules_sorted)
change_alphabet = _wrap_cxx_free_fn(_change_alphabet)
commutator = _wrap_cxx_free_fn(_commutator)
contains_rule = _wrap_cxx_free_fn(_contains_rule)
first_unused_letter = _wrap_cxx_free_fn(_first_unused_letter)
greedy_reduce_length = _wrap_cxx_free_fn(_greedy_reduce_length)
Expand Down
266 changes: 266 additions & 0 deletions src/present.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,272 @@ alphabet of *p*, and where :math:`z` is the second parameter.
:raises LibsemigroupsError: if *z* is not a letter in ``p.alphabet()``.

:complexity: Linear in the number of rules.)pbdoc");
m.def(
"presentation_commutator",
[](Presentation<Word> const& p, Word const& x, Word const& y) {
return presentation::commutator(p, x, y);
},
py::arg("p"),
py::arg("x"),
py::arg("y"),
R"pbdoc(
:sig=(p: Presentation, x: Word, y: Word) -> Word:
:only-document-once:

Return the commutator of two words.

Returns the word :math:`x^{-1}y^{-1}xy`, after attempting to detect inverses
from the rules in *p*, using :any:`try_detect_inverses`.

:param p: the presentation.
:type p: Presentation

:param x: the first word in the commutator.
:type x: :ref:`Word<pseudo_word_type_helper>`

:param y: the second word in the commutator.
:type y: :ref:`Word<pseudo_word_type_helper>`

:returns: The commutator :math:`x^{-1}y^{-1}xy`.
:rtype: :ref:`Word<pseudo_word_type_helper>`

:raises LibsemigroupsError:
if *x* or *y* contains a letter not belonging to ``p.alphabet()``, if
:any:`try_detect_inverses` throws, or if *x* or *y* contains a letter for
which no inverse was detected.
)pbdoc");
m.def(
"presentation_commutator",
[](Presentation<Word> const& p,
Word const& x,
Word const& y,
Word const& inverses) {
return presentation::commutator(p, x, y, inverses);
},
py::arg("p"),
py::arg("x"),
py::arg("y"),
py::arg("inverses"),
R"pbdoc(
:sig=(p: Presentation, x: Word, y: Word, inverses: Word) -> Word:
:only-document-once:

Return the commutator of two words.

Returns the word :math:`x^{-1}y^{-1}xy`. The letter ``a`` with index ``i`` in
*inverses* is the inverse of the letter in ``p.alphabet()`` with index ``i``.

:param p: the presentation.
:type p: Presentation

:param x: the first word in the commutator.
:type x: :ref:`Word<pseudo_word_type_helper>`

:param y: the second word in the commutator.
:type y: :ref:`Word<pseudo_word_type_helper>`

:param inverses: the inverses of the letters in p.alphabet().
:type inverses: :ref:`Word<pseudo_word_type_helper>`

:returns: The commutator :math:`x^{-1}y^{-1}xy`.
:rtype: :ref:`Word<pseudo_word_type_helper>`

:raises LibsemigroupsError:
if *inverses* are not valid inverses for ``p.alphabet()``, or if *x* or
*y* contains a letter not belonging to ``p.alphabet()``.
)pbdoc");
m.def(
"presentation_commutator",
[](Word const& x,
Word const& y,
Word const& alphabet,
Word const& inverses) {
return presentation::commutator(x, y, alphabet, inverses);
},
py::arg("x"),
py::arg("y"),
py::arg("alphabet"),
py::arg("inverses"),
R"pbdoc(
:sig=(x: Word, y: Word, alphabet: Word, inverses: Word) -> Word:
:only-document-once:

Return the commutator of two words.

Returns the word :math:`x^{-1}y^{-1}xy`. The letter ``a`` with index ``i`` in
*inverses* is the inverse of the letter in *alphabet* with index ``i``.

:param x: the first word in the commutator.
:type x: :ref:`Word<pseudo_word_type_helper>`

:param y: the second word in the commutator.
:type y: :ref:`Word<pseudo_word_type_helper>`

:param alphabet:
the alphabet, which should be a superset of the letters in x and y.
:type alphabet: :ref:`Word<pseudo_word_type_helper>`

:param inverses: the inverses of the letters in alphabet.
:type inverses: :ref:`Word<pseudo_word_type_helper>`

:returns: The commutator :math:`x^{-1}y^{-1}xy`.
:rtype: :ref:`Word<pseudo_word_type_helper>`

:raises LibsemigroupsError:
if *alphabet* contains duplicates, if *inverses* are not valid inverses
for *alphabet*, or if *x* or *y* contains a letter not belonging to
*alphabet*.
)pbdoc");
m.def(
"presentation_add_commutator_rule",
[](Presentation_& p,
Word const& x,
Word const& y,
Word const& alphabet,
Word const& inverses,
typename Presentation_::letter_type id) {
return presentation::add_commutator_rule(
p, x, y, alphabet, inverses, id);
},
py::arg("p"),
py::arg("x"),
py::arg("y"),
py::arg("alphabet"),
py::arg("inverses"),
py::kw_only(), // This is so id must be specified by a key-word
py::arg("id")
= static_cast<typename Presentation_::letter_type>(UNDEFINED),
R"pbdoc(
:sig=(p: Presentation, x: Word, y: Word, alphabet: Word, inverses: Word, *, id: Letter = UNDEFINED) -> None:
:only-document-once:

Add a commutator rule.

Adds the rule :math:`x^{-1}y^{-1}xy = id` to *p*. The letter ``a`` with index
``i`` in *inverses* is the inverse of the letter in *alphabet* with index
``i``. If *id* is :any:`UNDEFINED`, then the right-hand side is the empty
word.

:param p: the presentation.
:type p: Presentation

:param x: the first word in the commutator.
:type x: :ref:`Word<pseudo_word_type_helper>`

:param y: the second word in the commutator.
:type y: :ref:`Word<pseudo_word_type_helper>`

:param alphabet:
the alphabet, which should be a superset of the letters in *x* and *y*.
:type alphabet: :ref:`Word<pseudo_word_type_helper>`

:param inverses: the inverses of the letters in alphabet.
:type inverses: :ref:`Word<pseudo_word_type_helper>`

:param id:
the identity letter, or :any:`UNDEFINED` for the empty word. This is a
keyword-only argument.
:type id: :ref:`Letter<pseudo_letter_type_helper>`

:raises LibsemigroupsError:
if *alphabet*, *inverses*, *x*, *y*, or *id* contains a letter not
belonging to ``p.alphabet()``; if *alphabet* contains duplicates; if
*inverses* are not valid inverses for *alphabet*; or if *x* or *y*
contains a letter not belonging to *alphabet*.
)pbdoc");
m.def(
"presentation_add_commutator_rule",
[](Presentation_& p,
Word const& x,
Word const& y,
Word const& inverses,
typename Presentation_::letter_type id) {
return presentation::add_commutator_rule(p, x, y, inverses, id);
},
py::arg("p"),
py::arg("x"),
py::arg("y"),
py::arg("inverses"),
py::kw_only(), // This is so id must be specified by a key-word
py::arg("id")
= static_cast<typename Presentation_::letter_type>(UNDEFINED),
R"pbdoc(
:sig=(p: Presentation, x: Word, y: Word, inverses: Word, *, id: Letter = UNDEFINED) -> None:
:only-document-once:

Add a commutator rule.

Adds the rule :math:`x^{-1}y^{-1}xy = id` to *p*. The letter ``a`` with index
``i`` in *inverses* is the inverse of the letter in ``p.alphabet()`` with
index ``i``. If *id* is :any:`UNDEFINED`, then the right-hand side is the
empty word.

:param p: the presentation.
:type p: Presentation

:param x: the first word in the commutator.
:type x: :ref:`Word<pseudo_word_type_helper>`

:param y: the second word in the commutator.
:type y: :ref:`Word<pseudo_word_type_helper>`

:param inverses: the inverses of the letters in p.alphabet().
:type inverses: :ref:`Word<pseudo_word_type_helper>`

:param id:
the identity letter, or :any:`UNDEFINED` for the empty word. This is a
keyword-only argument.
:type id: :ref:`Letter<pseudo_letter_type_helper>`


:raises LibsemigroupsError:
if *inverses*, *x*, *y*, or *id* contains a letter not belonging to
``p.alphabet()``, or if *inverses* are not valid inverses for
``p.alphabet()``.
)pbdoc");
m.def(
"presentation_add_commutator_rule",
[](Presentation_& p,
Word const& x,
Word const& y,
typename Presentation_::letter_type id) {
return presentation::add_commutator_rule(p, x, y, id);
},
py::arg("p"),
py::arg("x"),
py::arg("y"),
py::kw_only(), // This is so id must be specified by a key-word
py::arg("id")
= static_cast<typename Presentation_::letter_type>(UNDEFINED),
R"pbdoc(
:sig=(p: Presentation, x: Word, y: Word, *, id: Letter = UNDEFINED) -> None:
:only-document-once:

Add a commutator rule.

Adds the rule :math:`x^{-1}y^{-1}xy = id` to *p*, after attempting to detect
inverses from the rules in *p*, using :any:`try_detect_inverses`. If
*id* is :any:`UNDEFINED`, then the right-hand side is the empty word.

:param p: the presentation.
:type p: Presentation

:param x: the first word in the commutator.
:type x: :ref:`Word<pseudo_word_type_helper>`

:param y: the second word in the commutator.
:type y: :ref:`Word<pseudo_word_type_helper>`

:param id:
the identity letter, or :any:`UNDEFINED` for the empty word. This is a
keyword-only argument.
:type id: :ref:`Letter<pseudo_letter_type_helper>`

:raises LibsemigroupsError:
if *x*, *y*, or *id* contains a letter not belonging to
``p.alphabet()``, if :any:`try_detect_inverses` throws, or if *x* or
*y* contains a letter for which no inverse was detected.
)pbdoc");
m.def(
"presentation_are_rules_sorted",
[](Presentation_ const& p) {
Expand Down
Loading