Skip to content

Commit 2fb1e23

Browse files
present+knuth-bendix: add missing features
1 parent 41a08fd commit 2fb1e23

File tree

6 files changed

+169
-33
lines changed

6 files changed

+169
-33
lines changed

docs/source/presentations/present-helpers.rst

Lines changed: 100 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,24 @@ Contents
3939
* - :py:func:`remove_duplicate_rules`
4040
- Remove duplicate rules.
4141

42+
* - :py:func:`remove_redundant_generators`
43+
- Remove any trivially redundant generators.
44+
45+
* - :py:func:`remove_trivial_rules`
46+
- Remove rules consisting of identical words.
47+
4248
* - :py:func:`reduce_complements`
43-
- If there are rules :math:`u = v` and :math:`v = w` where :math:`\lvert w \rvert < \lvert v \rvert`, then replace :math:`u = v` with :math:`u = w`.
49+
- If there are rules :math:`u = v` and :math:`v = w` where :math:`\lvert w
50+
\rvert < \lvert v \rvert`, then replace :math:`u = v` with :math:`u =
51+
w`.
4452

4553
* - :py:func:`sort_each_rule`
46-
- Sort each rule :math:`u = v` so that the left hand side is shortlex greater than the right hand side.
54+
- Sort each rule :math:`u = v` so that the left hand side is shortlex
55+
greater than the right hand side.
4756

4857
* - :py:func:`sort_rules`
49-
- Sort the rules :math:`u_1 = v_1, \ldots, u_n = v_n` so that :math:`u_1 v_1 < \cdots < u_n v_n`, where :math:`<` is the shortlex order.
58+
- Sort the rules :math:`u_1 = v_1, \ldots, u_n = v_n` so that :math:`u_1
59+
v_1 < \cdots < u_n v_n`, where :math:`<` is the shortlex order.
5060

5161
* - :py:func:`longest_common_subword`
5262
- Return the longest common subword of the rules.
@@ -192,8 +202,8 @@ Full API
192202
193203
Add rules for an identity element.
194204

195-
Adds rules of the form :math:`a e = e a = a` for every letter :math:`a` in the alphabet of
196-
``p``, where :math:`e` is the second parameter.
205+
Adds rules of the form :math:`a e = e a = a` for every letter :math:`a` in
206+
the alphabet of ``p``, where :math:`e` is the second parameter.
197207

198208
:param p: the presentation to add rules to
199209
:type p: Presentation
@@ -213,10 +223,11 @@ Full API
213223
214224
Add rules for inverses.
215225
216-
The letter ``a`` with index ``i`` in ``vals`` is the inverse of the letter in the alphabet
217-
of ``p`` with index ``i``. The rules added are :math:`a_i b_i = e`, where the alphabet is
218-
:math:`\{a_i, \ldots, a_n\}`; the parameter ``vals`` is :math:`\{b_1, \ldots, b_n\}`; and
219-
:math:`e` is the 3rd parameter.
226+
The letter ``a`` with index ``i`` in ``vals`` is the inverse of the letter
227+
in the alphabet of ``p`` with index ``i``. The rules added are :math:`a_i
228+
b_i = e`, where the alphabet is :math:`\{a_i, \ldots, a_n\}`; the parameter
229+
``vals`` is :math:`\{b_1, \ldots, b_n\}`; and :math:`e` is the 3rd
230+
parameter.
220231
221232
:param p: the presentation to add rules to
222233
:type p: Presentation
@@ -238,9 +249,10 @@ Full API
238249
239250
Remove duplicate rules.
240251
241-
Removes all but one instance of any duplicate rules (if any). Note that rules of the form
242-
:math:`u = v` and :math:`v = u` (if any) are considered duplicates. Also note that the
243-
rules may be reordered by this function even if there are no duplicate rules.
252+
Removes all but one instance of any duplicate rules (if any). Note that
253+
rules of the form :math:`u = v` and :math:`v = u` (if any) are considered
254+
duplicates. Also note that the rules may be reordered by this function even
255+
if there are no duplicate rules.
244256
245257
:param p: the presentation
246258
:type p: Presentation
@@ -257,15 +269,50 @@ Full API
257269
presentation.remove_duplicate_rules(p)
258270
p.rules # ['ab', 'baa']
259271
272+
.. py:function:: remove_redundant_generators(p: Presentation) -> None
273+
274+
Remove any trivially redundant generators.
275+
276+
If one side of any of the rules in the presentation ``p`` is a letter
277+
``a`` and the other side of the rule does not contain ``a``, then this
278+
function replaces every occurrence of ``a`` in every rule by the other
279+
side of the rule. This substitution is performed for every such
280+
rule in the presentation; and the trivial rules (with both sides being
281+
identical) are removed. If both sides of a rule are letters, then the
282+
greater letter is replaced by the lesser one.
283+
284+
:param p: the presentation
285+
:type p: Presentation
286+
287+
:returns: None
288+
289+
:raises RuntimeError: if ``len(p.rules)`` is odd.
290+
291+
.. py:function:: remove_trivial_rules(p: Presentation) -> None
292+
293+
Remove rules consisting of identical words.
294+
295+
Removes all instance of rules (if any) where the left hand side and the
296+
right hand side are identical.
297+
298+
:param p: the presentation
299+
:type p: Presentation
300+
301+
:returns: None
302+
303+
:raises RuntimeError: if ``len(p.rules)`` is odd.
304+
260305
.. py:function:: reduce_complements(p: Presentation) -> None
261306
262-
If there are rules :math:`u = v` and :math:`v = w` where :math:`\lvert w \rvert < \lvert v \rvert`, then replace :math:`u = v` with :math:`u = w`.
307+
If there are rules :math:`u = v` and :math:`v = w` where :math:`\lvert w
308+
\rvert < \lvert v \rvert`, then replace :math:`u = v` with :math:`u = w`.
263309
264-
Attempts to reduce the length of the words by finding the equivalence relation on the
265-
relation words generated by the pairs of identical relation words. If
266-
:math:`\{u_1, u_2, \ldots, u_n\}` are distinct words in an equivalence class and
267-
:math:`u_1` is the shortlex minimum word in the class, then the relation words are
268-
replaced by :math:`u_1 = u_2, u_1 = u_3, \ldots, u_1 = u_n`.
310+
Attempts to reduce the length of the words by finding the equivalence
311+
relation on the relation words generated by the pairs of identical relation
312+
words. If :math:`\{u_1, u_2, \ldots, u_n\}` are distinct words in an
313+
equivalence class and :math:`u_1` is the shortlex minimum word in the class,
314+
then the relation words are replaced by :math:`u_1 = u_2, u_1 = u_3, \ldots,
315+
u_1 = u_n`.
269316
270317
:param p: the presentation
271318
:type p: Presentation
@@ -284,8 +331,8 @@ Full API
284331
285332
.. py:function:: sort_each_rule(p: Presentation) -> None
286333
287-
Sort each rule :math:`u = v` so that the left hand side is shortlex greater than the right
288-
hand side.
334+
Sort each rule :math:`u = v` so that the left hand side is shortlex greater
335+
than the right hand side.
289336
290337
:param p: the presentation
291338
:type p: Presentation
@@ -306,24 +353,45 @@ Full API
306353
307354
Return the longest common subword of the rules.
308355
309-
If it is possible to find a subword :math:`w` of the rules
310-
:math:`u_1 = v_1, \ldots, u_n = v_n` such that the introduction of a new generator
311-
:math:`z` and the relation :math:`z = w` reduces the length (see :py:func:`length`)
312-
of the presentation, then this function returns the word :math:`w`. If no such word can be
313-
found, a word of length :math:`0` is returned.
356+
If it is possible to find a subword :math:`w` of the rules :math:`u_1 = v_1,
357+
\ldots, u_n = v_n` such that the introduction of a new generator :math:`z`
358+
and the relation :math:`z = w` reduces the length (see :py:func:`length`) of
359+
the presentation, then this function returns the word :math:`w`. If no such
360+
word can be found, a word of length :math:`0` is returned.
314361
315362
:param p: the presentation
316363
:type p: Presentation
317364
318365
:returns: None
319366
367+
.. py:function:: replace_subword(p: Presentation, existing: Union[str, List[int]], replacement: Union[str, List[int]])
368+
:noindex:
369+
370+
Replace non-overlapping instances of a subword by another word.
371+
372+
If ``existing`` and ``replacement`` are words, then this function replaces
373+
every non-overlapping instance of ``existing`` in every rule by
374+
``replacement``. The presentation ``p`` is changed in-place.
375+
376+
:param p: the presentation
377+
:type p: Presentation
378+
:param existing: the word to be replaced
379+
:type existing: str or List[int]
380+
:param replacement: the replacement word.
381+
:type replacement: str or List[int]
382+
383+
:returns: None
384+
385+
:raises RuntimeError: if ``existing`` is empty.
386+
320387
.. py:function:: replace_subword(p: Presentation, w: Union[str, List[int]])
321388
322389
Replace non-overlapping instances of a subword.
323390
324-
A new generator :math:`z` is added to the presentation, along with the rule :math:`w = z`.
325-
Each (if any) non-overlapping instance (from left to right) of the word :math:`w` in every
326-
rule of the presentation is replaced with :math:`z`.
391+
A new generator :math:`z` is added to the presentation, along with the rule
392+
:math:`w = z`. Each (if any) non-overlapping instance (from left to right)
393+
of the word :math:`w` in every rule of the presentation is replaced with
394+
:math:`z`.
327395
328396
:param p: the presentation
329397
:type p: Presentation
@@ -359,10 +427,11 @@ Full API
359427
360428
.. py:function:: normalize_alphabet(p: Presentation) -> None
361429
362-
Modify the presentation so that the alphabet is :math:`\{0, \ldots, n - 1\}` (or equivalent),
363-
and rewrites the rules to use this alphabet.
430+
Modify the presentation so that the alphabet is :math:`\{0, \ldots, n - 1\}`
431+
(or equivalent), and rewrites the rules to use this alphabet.
364432
365-
If the alphabet is already normalized, then no changes are made to the presentation.
433+
If the alphabet is already normalized, then no changes are made to the
434+
presentation.
366435
367436
:param p: the presentation
368437
:type p: Presentation

libsemigroups_pybind11/presentation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
add_identity_rules,
2323
add_inverse_rules,
2424
remove_duplicate_rules,
25+
remove_trivial_rules,
26+
remove_redundant_generators,
2527
reduce_complements,
2628
sort_each_rule,
2729
sort_rules,

src/knuth-bendix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,20 @@ namespace libsemigroups {
691691
:Parameters: None
692692
693693
:return: A copy of an :py:class:`ActionDigraph`.
694+
)pbdoc")
695+
.def("rewrite",
696+
py::overload_cast<std::string>(&fpsemigroup::KnuthBendix::rewrite,
697+
py::const_),
698+
R"pbdoc(
699+
Rewrite a word.
700+
701+
Rewrites a copy of the string ``w`` rewritten according to the current
702+
rules in the ``KnuthBendix`` instance.
703+
704+
:param w: the word to rewrite.
705+
:type w: str
706+
707+
:returns: A copy of the argument ``w`` after it has been rewritten.
694708
)pbdoc");
695709
}
696710
} // namespace libsemigroups

src/present.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,24 @@ namespace libsemigroups {
9898
m.def("add_identity_rules", &presentation::add_identity_rules<T>);
9999
m.def("add_inverse_rules", &presentation::add_inverse_rules<T>);
100100
m.def("remove_duplicate_rules", &presentation::remove_duplicate_rules<T>);
101+
m.def("remove_trivial_rules", &presentation::remove_trivial_rules<T>);
101102
m.def("reduce_complements", &presentation::reduce_complements<T>);
102103
m.def("sort_each_rule", &presentation::sort_each_rule<T>);
103104
m.def("sort_rules", &presentation::sort_rules<T>);
104105
m.def("longest_common_subword", &presentation::longest_common_subword<T>);
105106
m.def("replace_subword",
106107
py::overload_cast<Presentation<T>&, T const&>(
107108
&presentation::replace_subword<T>));
109+
m.def(
110+
"replace_subword",
111+
[](Presentation<T>& p, T const& existing, T const& replace) -> void {
112+
presentation::replace_subword(p, existing, replace);
113+
});
108114
m.def("length", &presentation::length<T>);
109115
m.def("reverse", &presentation::reverse<T>);
110116
m.def("normalize_alphabet", &presentation::normalize_alphabet<T>);
117+
m.def("remove_redundant_generators",
118+
&presentation::remove_redundant_generators<T>);
111119

112120
m.def(
113121
"make",

tests/fpsemi_intf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,10 @@ def check_operators(t):
184184
x.normal_form("z")
185185

186186
if hasattr(t, "rewrite"):
187-
assert x.rewrite("aa") == ""
187+
assert x.rewrite("aa") == "e"
188188
assert x.rewrite("bb") == "B"
189189

190-
with pytest.raises(TypeError):
190+
with pytest.raises(RuntimeError):
191191
x.rewrite("z")
192192

193193

tests/test_present.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,21 @@ def check_longest_common_subword(W):
248248
W([p.letter(3)]),
249249
W([1, 2, 1]),
250250
]
251+
presentation.replace_subword(p, W([1, 2, 1]), W([1]))
252+
assert p.rules == [
253+
W([0, p.letter(3)]),
254+
W([p.letter(3)]),
255+
W([p.letter(3)]),
256+
W([1, p.letter(3)]),
257+
W([1, p.letter(3)]),
258+
W([1, 1]),
259+
W([1, 1]),
260+
W([p.letter(3)]),
261+
W([p.letter(3)]),
262+
W([0]),
263+
W([p.letter(3)]),
264+
W([1]),
265+
]
251266

252267

253268
def check_redundant_rule(W):
@@ -677,3 +692,31 @@ def test_issue_52_022():
677692
p.validate()
678693

679694
assert presentation.redundant_rule(p, timedelta(seconds=1)) == len(p.rules)
695+
696+
697+
def test_helpers_remove_trivial_rules():
698+
p = Presentation([])
699+
presentation.add_rule(p, [0], [0])
700+
presentation.add_rule(p, [1], [1])
701+
presentation.add_rule(p, [1, 0, 1], [1])
702+
p.alphabet_from_rules()
703+
p.validate()
704+
705+
presentation.remove_trivial_rules(p)
706+
707+
assert p.rules == [[1, 0, 1], [1]]
708+
709+
710+
def test_helpers_remove_redundant_generators():
711+
p = Presentation([])
712+
presentation.add_rule(p, [0], [1])
713+
presentation.add_rule(p, [1], [1])
714+
presentation.add_rule(p, [1, 0, 1], [1])
715+
p.alphabet_from_rules()
716+
p.validate()
717+
718+
presentation.remove_redundant_generators(p)
719+
assert p.rules == [[0, 0, 0], [0]]
720+
p.rules = p.rules + [[1, 1, 1], [0]]
721+
presentation.remove_redundant_generators(p)
722+
assert p.rules == [[1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1]]

0 commit comments

Comments
 (0)