Skip to content

Commit 3b79c4b

Browse files
todd-coxeter: support lookbehind
1 parent 32c8c25 commit 3b79c4b

3 files changed

Lines changed: 67 additions & 1 deletion

File tree

docs/source/main-algorithms/todd-coxeter/helpers.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Contents
2525
non_trivial_classes
2626
normal_forms
2727
partition
28+
perform_lookbehind
2829
redundant_rule
2930

3031
Full API

src/libsemigroups_pybind11/todd_coxeter.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
todd_coxeter_non_trivial_classes as _todd_coxeter_non_trivial_classes,
2626
todd_coxeter_normal_forms as _todd_coxeter_normal_forms,
2727
todd_coxeter_partition as _todd_coxeter_partition,
28+
todd_coxeter_perform_lookbehind as _todd_coxeter_perform_lookbehind,
2829
todd_coxeter_redundant_rule as _todd_coxeter_redundant_rule,
2930
)
3031

@@ -92,6 +93,7 @@ def __init__(self: _Self, *args, **kwargs) -> None:
9293
non_trivial_classes = _wrap_cxx_free_fn(_todd_coxeter_non_trivial_classes)
9394
normal_forms = _wrap_cxx_free_fn(_todd_coxeter_normal_forms)
9495
partition = _wrap_cxx_free_fn(_todd_coxeter_partition)
96+
perform_lookbehind = _wrap_cxx_free_fn(_todd_coxeter_perform_lookbehind)
9597
redundant_rule = _wrap_cxx_free_fn(_todd_coxeter_redundant_rule)
9698

9799
########################################################################

src/todd-coxeter.cpp

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,8 +553,71 @@ non-trivial congruence containing the congruence represented by a
553553
:returns: Whether or not a non-trivial quotient was found.
554554
:rtype: tril
555555
)pbdoc");
556+
557+
m.def("todd_coxeter_perform_lookbehind",
558+
&todd_coxeter::perform_lookbehind,
559+
py::arg("tc"),
560+
R"pbdoc(
561+
:sig=(tc: ToddCoxeter) -> None:
562+
:only-document-once:
563+
564+
Perform a lookbehind.
565+
566+
This function performs a "lookbehind" on the argument *tc* which is
567+
defined as follows. For every node ``n`` in the so-far computed
568+
:any:`WordGraph` (obtained from :any:`ToddCoxeter.current_word_graph`) we
569+
use the current word graph to rewrite the current short-lex least path
570+
from the initial node to ``n``. If this rewritten word is not equal to
571+
the original word, and it also labels a path from the initial node in
572+
the current word graph to a node ``m``, then ``m`` and ``n`` represent the
573+
same congruence class. Thus we may collapse ``m`` and ``n`` (i.e. quotient
574+
the word graph by the least congruence containing the pair ``m`` and
575+
``n``).
576+
577+
The intended use case for this function is when you have a large word
578+
graph in a partially enumerated :any:`ToddCoxeter` instance, and you
579+
would like to minimise this word graph as far as possible.
580+
581+
For example, if we take the following monoid presentation of B. H.
582+
Neumann for the trivial group:
583+
584+
.. code-block:: python
585+
586+
p = Presentation("abcdef")
587+
p.contains_empty_word(true)
588+
presentation.add_inverse_rules(p, "defabc")
589+
presentation.add_rule(p, "bbdeaecbffdbaeeccefbccefb", "")
590+
presentation.add_rule(p, "ccefbfacddecbffaafdcaafdc", "")
591+
presentation.add_rule(p, "aafdcdbaeefacddbbdeabbdea", "")
592+
tc = ToddCoxeter(congruence_kind.twosided, p)
593+
594+
Then running *tc* will simply grow the underlying word graph until
595+
your computer runs out of memory. The authors of ``libsemigroups`` were
596+
not able to find any combination of the many settings for
597+
:any:`ToddCoxeter` where running *tc* returned an answer. We also tried
598+
with GAP and ACE but neither of these seemed able to return an answer
599+
either. But doing the following:
600+
601+
.. code-block:: python
602+
603+
tc.lookahead_extent(options.lookahead_extent.full)
604+
.lookahead_style(options.lookahead_style.felsch)
605+
606+
tc.run_for(timedelta(seconds=1))
607+
tc.perform_lookahead(True)
608+
todd_coxeter.perform_lookbehind(tc)
609+
tc.run_for(timedelta(seconds=1))
610+
todd_coxeter.perform_lookbehind(tc)
611+
tc.perform_lookahead(True)
612+
tc.number_of_classes() # returns 1
613+
614+
returns the correct answer in about 22 seconds (on a 2024 Macbook Pro M4
615+
Pro).
616+
617+
:param tc: the :any:`ToddCoxeter` instance.
618+
:type tc: ToddCoxeter)pbdoc");
556619
} // bind_todd_coxeter
557-
} // namespace
620+
} // namespace
558621

559622
void init_todd_coxeter(py::module& m) {
560623
bind_todd_coxeter<word_type>(m, "ToddCoxeterWord");

0 commit comments

Comments
 (0)