From 2e1e510d04d7e39f3d80f3aaa8e6bdd295692cfc Mon Sep 17 00:00:00 2001 From: Codex OpenAI Date: Thu, 13 Aug 2026 16:01:57 +0100 Subject: [PATCH 1/2] presentation: update to_gap_string with default arg --- src/present.cpp | 9 ++-- tests/test_present.py | 119 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 103 insertions(+), 25 deletions(-) diff --git a/src/present.cpp b/src/present.cpp index 16bc2157..2965b609 100644 --- a/src/present.cpp +++ b/src/present.cpp @@ -1581,16 +1581,18 @@ modified version. * :any:`is_strongly_compressible` )pbdoc"); + m.def( "presentation_to_gap_string", [](Presentation_ const& p, std::string const& var_name) { return presentation::to_gap_string(p, var_name); }, py::arg("p"), - py::arg("var_name"), + py::arg("var_name") = "S", R"pbdoc( :sig=(p: Presentation, var_name: str) -> str: :only-document-once: + Return the code that would create *p* in GAP. This function returns the string of GAP code that could be used to create an @@ -1600,12 +1602,13 @@ are created by taking quotients of free semigroups or monoids. :param p: the presentation. :type p: Presentation -:param var_name: the name of the variable to be used in GAP. +:param var_name: the name of the variable to be used in GAP (defaults to ``"S"`` . :type var_name: str :returns: The GAP string. :rtype: str )pbdoc"); + m.def( "presentation_to_ace_string", [](Presentation_ const& p) { return presentation::to_ace_string(p); }, @@ -2087,7 +2090,7 @@ defined in the alphabet, and that the inverses act as semigroup inverses. * :any:`presentation.throw_if_bad_inverses` )pbdoc"); } // bind_inverse_present - } // namespace + } // namespace void init_present(py::module& m) { bind_present(m, "PresentationWord"); diff --git a/tests/test_present.py b/tests/test_present.py index 8142eee7..f4a7b7b6 100644 --- a/tests/test_present.py +++ b/tests/test_present.py @@ -167,7 +167,12 @@ def check_add_rules(W): W([4, 1]), W([0, 1, 1, 1, 1, 1, 1, 1, 1, 1]), ] - assert q.rules == [W([4, 1]), W([0, 5]), W([4, 1]), W([0, 1, 1, 1, 1, 1, 1, 1, 1, 1])] + assert q.rules == [ + W([4, 1]), + W([0, 5]), + W([4, 1]), + W([0, 1, 1, 1, 1, 1, 1, 1, 1, 1]), + ] assert presentation.contains_rule(p, W([4, 1]), W([0, 5])) assert not presentation.contains_rule(p, W([0, 0]), W([4, 1])) p.throw_if_bad_alphabet_or_rules() @@ -291,25 +296,39 @@ def check_commutator(W): assert presentation.commutator(W([0]), W([]), W([0]), W([1])) == W([1, 0]) assert presentation.commutator(W([]), W([0]), W([0]), W([1])) == W([1, 0]) - assert presentation.commutator(W([0, 1]), W([]), W([0, 1]), W([2, 3])) == W([3, 2, 0, 1]) - assert presentation.commutator(W([]), W([0, 1]), W([0, 1]), W([2, 3])) == W([3, 2, 0, 1]) - assert presentation.commutator(W([]), W([0, 1]), W([0, 1]), W([1, 0])) == W([0, 1, 0, 1]) - assert presentation.commutator(W([0, 1]), W([]), W([0, 1]), W([1, 0])) == W([0, 1, 0, 1]) - - assert presentation.commutator(W([0, 1, 2]), W([1, 0, 1]), W([0, 1, 2]), W([3, 4, 5])) == W( - [5, 4, 3, 4, 3, 4, 0, 1, 2, 1, 0, 1] + assert presentation.commutator(W([0, 1]), W([]), W([0, 1]), W([2, 3])) == W( + [3, 2, 0, 1] ) - assert presentation.commutator(W([0, 1, 2]), W([1, 0, 1]), W([0, 1, 2]), W([2, 1, 0])) == W( - [0, 1, 2, 1, 2, 1, 0, 1, 2, 1, 0, 1] + assert presentation.commutator(W([]), W([0, 1]), W([0, 1]), W([2, 3])) == W( + [3, 2, 0, 1] ) + assert presentation.commutator(W([]), W([0, 1]), W([0, 1]), W([1, 0])) == W( + [0, 1, 0, 1] + ) + assert presentation.commutator(W([0, 1]), W([]), W([0, 1]), W([1, 0])) == W( + [0, 1, 0, 1] + ) + + assert presentation.commutator( + W([0, 1, 2]), W([1, 0, 1]), W([0, 1, 2]), W([3, 4, 5]) + ) == W([5, 4, 3, 4, 3, 4, 0, 1, 2, 1, 0, 1]) + assert presentation.commutator( + W([0, 1, 2]), W([1, 0, 1]), W([0, 1, 2]), W([2, 1, 0]) + ) == W([0, 1, 2, 1, 2, 1, 0, 1, 2, 1, 0, 1]) # alphabet inferred, inverses specified assert presentation.commutator(p, W([]), W([]), W([])) == W([]) p.alphabet(W([0, 1, 2])) - assert presentation.commutator(p, W([0, 1]), W([1]), W([1, 0, 2])) == W([0, 1, 0, 0, 1, 1]) - assert presentation.commutator(p, W([0, 1]), W([1]), W([0, 1, 2])) == W([1, 0, 1, 0, 1, 1]) - assert presentation.commutator(p, W([0, 1]), W([1]), W([0, 2, 1])) == W([2, 0, 2, 0, 1, 1]) + assert presentation.commutator(p, W([0, 1]), W([1]), W([1, 0, 2])) == W( + [0, 1, 0, 0, 1, 1] + ) + assert presentation.commutator(p, W([0, 1]), W([1]), W([0, 1, 2])) == W( + [1, 0, 1, 0, 1, 1] + ) + assert presentation.commutator(p, W([0, 1]), W([1]), W([0, 2, 1])) == W( + [2, 0, 2, 0, 1, 1] + ) # alphabet inferred, inverses inferred assert presentation.commutator(p, W([]), W([])) == W([]) @@ -365,7 +384,9 @@ def check_add_commutator_rule_errors(W): # pylint: disable=too-many-statements # The id is not in the presentation's alphabet with pytest.raises(LibsemigroupsError): - presentation.add_commutator_rule(p, W([]), W([]), W([0, 1, 2]), W([2, 1, 0]), id=W([4])[0]) + presentation.add_commutator_rule( + p, W([]), W([]), W([0, 1, 2]), W([2, 1, 0]), id=W([4])[0] + ) p.init() p.contains_empty_word(True) @@ -437,7 +458,9 @@ def check_add_commutator_rule(W): p = Presentation(W([0, 1, 2, 3])) p.contains_empty_word(True) presentation.add_commutator_rule(p, W([0]), W([1]), W([0, 1]), W([2, 3])) - presentation.add_commutator_rule(p, W([2, 0]), W([1]), W([2, 1, 0]), W([0, 3, 2]), id=W([0])[0]) + presentation.add_commutator_rule( + p, W([2, 0]), W([1]), W([2, 1, 0]), W([0, 3, 2]), id=W([0])[0] + ) assert p.rules == [W([2, 3, 0, 1]), W([]), W([2, 0, 3, 2, 0, 1]), W([0])] @@ -445,7 +468,9 @@ def check_add_commutator_rule(W): p.contains_empty_word(True) p.alphabet(W([0, 1, 2, 3])) presentation.add_commutator_rule(p, W([0]), W([1]), W([2, 3, 0, 1])) - presentation.add_commutator_rule(p, W([2, 0]), W([1]), W([2, 3, 0, 1]), id=W([0])[0]) + presentation.add_commutator_rule( + p, W([2, 0]), W([1]), W([2, 3, 0, 1]), id=W([0])[0] + ) assert p.rules == [W([2, 3, 0, 1]), W([]), W([2, 0, 3, 2, 0, 1]), W([0])] @@ -809,14 +834,37 @@ def check_to_ace_string(W): presentation.add_rule(p, W([0, 0]), W([])) presentation.add_rule(p, W([1, 1, 1]), W([])) presentation.add_rule(p, W([0, 1, 0, 1]), W([])) - assert ( - presentation.to_ace_string(p) - == """Group: a, b; + assert presentation.to_ace_string(p) == """Group: a, b; wo: 4g; # workspace size, adjust as necessary Rel: aa, bbb, abab; Mess: 100000; # message frequency, adjust as necessary End;""" - ) + + +def check_to_gap_string(W): + p = Presentation(W([0, 1])) + presentation.add_rule(p, W([0, 1]), W([1, 0])) + + a, b = ("s0", "s1") if W is to_word else ("a", "b") + assert presentation.to_gap_string(p) == f"""F := FreeSemigroup("{a}", "{b}"); +AssignGeneratorVariables(F);; +R := [ + [{a} * {b}, {b} * {a}] + ]; +S := F / R; +""" + + a, b = ("m0", "m1") if W is to_word else ("a", "b") + p.contains_empty_word(True) + presentation.add_rule(p, W([0, 0]), W([])) + assert presentation.to_gap_string(p, "M") == f"""F := FreeMonoid("{a}", "{b}"); +AssignGeneratorVariables(F);; +R := [ + [{a} * {b}, {b} * {a}], + [{a} * {a}, One(F)] + ]; +M := F / R; +""" ############################################################################### @@ -944,7 +992,20 @@ def test_helpers_add_identity_rules_std_string_012(): p = Presentation("abc") presentation.add_rule(p, "abcb", "aa") presentation.add_identity_rules(p, "a") - assert p.rules == ["abcb", "aa", "aa", "a", "ba", "b", "ab", "b", "ca", "c", "ac", "c"] + assert p.rules == [ + "abcb", + "aa", + "aa", + "a", + "ba", + "b", + "ab", + "b", + "ca", + "c", + "ac", + "c", + ] def test_helpers_add_inverse_rules_013(): @@ -1042,7 +1103,16 @@ def test_helpers_reduce_complements_017(): presentation.reduce_complements(p) presentation.sort_each_rule(p) presentation.sort_rules(p) - assert p.rules == [[1, 1], [0], [1, 2, 1], [0], [0, 1, 2, 1], [0], [1, 1, 2, 1], [0]] + assert p.rules == [ + [1, 1], + [0], + [1, 2, 1], + [0], + [0, 1, 2, 1], + [0], + [1, 1, 2, 1], + [0], + ] p = Presentation("abc") presentation.add_rule(p, "abcb", "bcb") @@ -1780,3 +1850,8 @@ def test_add_idempotent_rules(): def test_to_ace_string(): check_to_ace_string(to_word) check_to_ace_string(to_string) + + +def test_to_gap_string(): + check_to_gap_string(to_word) + check_to_gap_string(to_string) From bc99d77401b5fe8e4ebfb23ceb9832c5e53f92ae Mon Sep 17 00:00:00 2001 From: Codex OpenAI Date: Thu, 13 Aug 2026 16:06:12 +0100 Subject: [PATCH 2/2] presentation: add manual example for to_gap_string --- src/present.cpp | 17 +++++++- tests/test_present.py | 98 +++++++++++++------------------------------ 2 files changed, 45 insertions(+), 70 deletions(-) diff --git a/src/present.cpp b/src/present.cpp index 2965b609..727fe07a 100644 --- a/src/present.cpp +++ b/src/present.cpp @@ -1602,11 +1602,24 @@ are created by taking quotients of free semigroups or monoids. :param p: the presentation. :type p: Presentation -:param var_name: the name of the variable to be used in GAP (defaults to ``"S"`` . +:param var_name: the name of the variable to be used in GAP (defaults to ``"S"``). :type var_name: str :returns: The GAP string. :rtype: str + +.. doctest:: + + >>> from libsemigroups_pybind11 import Presentation, presentation + >>> p = Presentation("ab") + >>> presentation.add_rule(p, "ab", "ba") + >>> print(presentation.to_gap_string(p), end="") + F := FreeSemigroup("a", "b"); + AssignGeneratorVariables(F);; + R := [ + [a * b, b * a] + ]; + S := F / R; )pbdoc"); m.def( @@ -2090,7 +2103,7 @@ defined in the alphabet, and that the inverses act as semigroup inverses. * :any:`presentation.throw_if_bad_inverses` )pbdoc"); } // bind_inverse_present - } // namespace + } // namespace void init_present(py::module& m) { bind_present(m, "PresentationWord"); diff --git a/tests/test_present.py b/tests/test_present.py index f4a7b7b6..bb431644 100644 --- a/tests/test_present.py +++ b/tests/test_present.py @@ -167,12 +167,7 @@ def check_add_rules(W): W([4, 1]), W([0, 1, 1, 1, 1, 1, 1, 1, 1, 1]), ] - assert q.rules == [ - W([4, 1]), - W([0, 5]), - W([4, 1]), - W([0, 1, 1, 1, 1, 1, 1, 1, 1, 1]), - ] + assert q.rules == [W([4, 1]), W([0, 5]), W([4, 1]), W([0, 1, 1, 1, 1, 1, 1, 1, 1, 1])] assert presentation.contains_rule(p, W([4, 1]), W([0, 5])) assert not presentation.contains_rule(p, W([0, 0]), W([4, 1])) p.throw_if_bad_alphabet_or_rules() @@ -296,39 +291,25 @@ def check_commutator(W): assert presentation.commutator(W([0]), W([]), W([0]), W([1])) == W([1, 0]) assert presentation.commutator(W([]), W([0]), W([0]), W([1])) == W([1, 0]) - assert presentation.commutator(W([0, 1]), W([]), W([0, 1]), W([2, 3])) == W( - [3, 2, 0, 1] - ) - assert presentation.commutator(W([]), W([0, 1]), W([0, 1]), W([2, 3])) == W( - [3, 2, 0, 1] - ) - assert presentation.commutator(W([]), W([0, 1]), W([0, 1]), W([1, 0])) == W( - [0, 1, 0, 1] + assert presentation.commutator(W([0, 1]), W([]), W([0, 1]), W([2, 3])) == W([3, 2, 0, 1]) + assert presentation.commutator(W([]), W([0, 1]), W([0, 1]), W([2, 3])) == W([3, 2, 0, 1]) + assert presentation.commutator(W([]), W([0, 1]), W([0, 1]), W([1, 0])) == W([0, 1, 0, 1]) + assert presentation.commutator(W([0, 1]), W([]), W([0, 1]), W([1, 0])) == W([0, 1, 0, 1]) + + assert presentation.commutator(W([0, 1, 2]), W([1, 0, 1]), W([0, 1, 2]), W([3, 4, 5])) == W( + [5, 4, 3, 4, 3, 4, 0, 1, 2, 1, 0, 1] ) - assert presentation.commutator(W([0, 1]), W([]), W([0, 1]), W([1, 0])) == W( - [0, 1, 0, 1] + assert presentation.commutator(W([0, 1, 2]), W([1, 0, 1]), W([0, 1, 2]), W([2, 1, 0])) == W( + [0, 1, 2, 1, 2, 1, 0, 1, 2, 1, 0, 1] ) - assert presentation.commutator( - W([0, 1, 2]), W([1, 0, 1]), W([0, 1, 2]), W([3, 4, 5]) - ) == W([5, 4, 3, 4, 3, 4, 0, 1, 2, 1, 0, 1]) - assert presentation.commutator( - W([0, 1, 2]), W([1, 0, 1]), W([0, 1, 2]), W([2, 1, 0]) - ) == W([0, 1, 2, 1, 2, 1, 0, 1, 2, 1, 0, 1]) - # alphabet inferred, inverses specified assert presentation.commutator(p, W([]), W([]), W([])) == W([]) p.alphabet(W([0, 1, 2])) - assert presentation.commutator(p, W([0, 1]), W([1]), W([1, 0, 2])) == W( - [0, 1, 0, 0, 1, 1] - ) - assert presentation.commutator(p, W([0, 1]), W([1]), W([0, 1, 2])) == W( - [1, 0, 1, 0, 1, 1] - ) - assert presentation.commutator(p, W([0, 1]), W([1]), W([0, 2, 1])) == W( - [2, 0, 2, 0, 1, 1] - ) + assert presentation.commutator(p, W([0, 1]), W([1]), W([1, 0, 2])) == W([0, 1, 0, 0, 1, 1]) + assert presentation.commutator(p, W([0, 1]), W([1]), W([0, 1, 2])) == W([1, 0, 1, 0, 1, 1]) + assert presentation.commutator(p, W([0, 1]), W([1]), W([0, 2, 1])) == W([2, 0, 2, 0, 1, 1]) # alphabet inferred, inverses inferred assert presentation.commutator(p, W([]), W([])) == W([]) @@ -384,9 +365,7 @@ def check_add_commutator_rule_errors(W): # pylint: disable=too-many-statements # The id is not in the presentation's alphabet with pytest.raises(LibsemigroupsError): - presentation.add_commutator_rule( - p, W([]), W([]), W([0, 1, 2]), W([2, 1, 0]), id=W([4])[0] - ) + presentation.add_commutator_rule(p, W([]), W([]), W([0, 1, 2]), W([2, 1, 0]), id=W([4])[0]) p.init() p.contains_empty_word(True) @@ -458,9 +437,7 @@ def check_add_commutator_rule(W): p = Presentation(W([0, 1, 2, 3])) p.contains_empty_word(True) presentation.add_commutator_rule(p, W([0]), W([1]), W([0, 1]), W([2, 3])) - presentation.add_commutator_rule( - p, W([2, 0]), W([1]), W([2, 1, 0]), W([0, 3, 2]), id=W([0])[0] - ) + presentation.add_commutator_rule(p, W([2, 0]), W([1]), W([2, 1, 0]), W([0, 3, 2]), id=W([0])[0]) assert p.rules == [W([2, 3, 0, 1]), W([]), W([2, 0, 3, 2, 0, 1]), W([0])] @@ -468,9 +445,7 @@ def check_add_commutator_rule(W): p.contains_empty_word(True) p.alphabet(W([0, 1, 2, 3])) presentation.add_commutator_rule(p, W([0]), W([1]), W([2, 3, 0, 1])) - presentation.add_commutator_rule( - p, W([2, 0]), W([1]), W([2, 3, 0, 1]), id=W([0])[0] - ) + presentation.add_commutator_rule(p, W([2, 0]), W([1]), W([2, 3, 0, 1]), id=W([0])[0]) assert p.rules == [W([2, 3, 0, 1]), W([]), W([2, 0, 3, 2, 0, 1]), W([0])] @@ -834,11 +809,14 @@ def check_to_ace_string(W): presentation.add_rule(p, W([0, 0]), W([])) presentation.add_rule(p, W([1, 1, 1]), W([])) presentation.add_rule(p, W([0, 1, 0, 1]), W([])) - assert presentation.to_ace_string(p) == """Group: a, b; + assert ( + presentation.to_ace_string(p) + == """Group: a, b; wo: 4g; # workspace size, adjust as necessary Rel: aa, bbb, abab; Mess: 100000; # message frequency, adjust as necessary End;""" + ) def check_to_gap_string(W): @@ -846,18 +824,23 @@ def check_to_gap_string(W): presentation.add_rule(p, W([0, 1]), W([1, 0])) a, b = ("s0", "s1") if W is to_word else ("a", "b") - assert presentation.to_gap_string(p) == f"""F := FreeSemigroup("{a}", "{b}"); + assert ( + presentation.to_gap_string(p) + == f"""F := FreeSemigroup("{a}", "{b}"); AssignGeneratorVariables(F);; R := [ [{a} * {b}, {b} * {a}] ]; S := F / R; """ + ) a, b = ("m0", "m1") if W is to_word else ("a", "b") p.contains_empty_word(True) presentation.add_rule(p, W([0, 0]), W([])) - assert presentation.to_gap_string(p, "M") == f"""F := FreeMonoid("{a}", "{b}"); + assert ( + presentation.to_gap_string(p, "M") + == f"""F := FreeMonoid("{a}", "{b}"); AssignGeneratorVariables(F);; R := [ [{a} * {b}, {b} * {a}], @@ -865,6 +848,7 @@ def check_to_gap_string(W): ]; M := F / R; """ + ) ############################################################################### @@ -992,20 +976,7 @@ def test_helpers_add_identity_rules_std_string_012(): p = Presentation("abc") presentation.add_rule(p, "abcb", "aa") presentation.add_identity_rules(p, "a") - assert p.rules == [ - "abcb", - "aa", - "aa", - "a", - "ba", - "b", - "ab", - "b", - "ca", - "c", - "ac", - "c", - ] + assert p.rules == ["abcb", "aa", "aa", "a", "ba", "b", "ab", "b", "ca", "c", "ac", "c"] def test_helpers_add_inverse_rules_013(): @@ -1103,16 +1074,7 @@ def test_helpers_reduce_complements_017(): presentation.reduce_complements(p) presentation.sort_each_rule(p) presentation.sort_rules(p) - assert p.rules == [ - [1, 1], - [0], - [1, 2, 1], - [0], - [0, 1, 2, 1], - [0], - [1, 1, 2, 1], - [0], - ] + assert p.rules == [[1, 1], [0], [1, 2, 1], [0], [0, 1, 2, 1], [0], [1, 1, 2, 1], [0]] p = Presentation("abc") presentation.add_rule(p, "abcb", "bcb")