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
1 change: 1 addition & 0 deletions dev-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ dependencies:
- sphinxcontrib-bibtex
- pip:
- accepts
- pkgconfig
8 changes: 8 additions & 0 deletions docs/source/main-algorithms/todd-coxeter/class/accessors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ This page contains the documentation of the various member functions of the
Those functions with the prefix ``current_`` do not perform any further
enumeration.

.. automethod:: ToddCoxeter.complete

.. automethod:: ToddCoxeter.current_spanning_tree

.. automethod:: ToddCoxeter.current_word_graph

.. automethod:: ToddCoxeter.is_standardized

.. automethod:: ToddCoxeter.number_of_edges_active

.. automethod:: ToddCoxeter.number_of_large_collapses

.. automethod:: ToddCoxeter.number_of_nodes_active

.. automethod:: ToddCoxeter.spanning_tree

.. automethod:: ToddCoxeter.standardization_order
Expand Down
3 changes: 1 addition & 2 deletions src/aho-corasick.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

// C++ stl headers....
#include <string> // for string
#include <vector> // for vector

// libsemigroups....
#include <libsemigroups/aho-corasick.hpp> // for AhoCorasick, AhoCorasick::...
Expand Down Expand Up @@ -289,7 +288,7 @@ node; either active or inactive.
)pbdoc");

thing.def(
"is_terminal",
"is_terminal", // TODO rename "terminal"
[](AhoCorasick const& ac, size_t i) {
ac.throw_if_node_index_out_of_range(i);
ac.throw_if_node_index_not_active(i);
Expand Down
176 changes: 166 additions & 10 deletions src/todd-coxeter-impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,32 +67,32 @@ The valid values are :

.. py:attribute:: strategy.hlt
:value: <strategy.hlt: 0>

This value indicates that the HLT (Hazelgrove-Leech-Trotter) strategy should be used. This is analogous to ACE's R-style.

.. py:attribute:: strategy.felsch
:value: <strategy.felsch: 1>

This value indicates that the Felsch strategy should be used. This is analogous to ACE's C-style.

.. py:attribute:: strategy.CR
:value: <strategy.CR: 2>

This strategy is meant to mimic the ACE strategy of the same name. The Felsch is run until at least :any:`f_defs` nodes are defined, then the HLT strategy is run until at least :any:`hlt_defs` divided by :math:`N` nodes have been defined, where :math:`N` is the sum of the lengths of the words in the presentation and generating pairs. These steps are repeated until the enumeration terminates.

.. py:attribute:: strategy.R_over_C
:value: <strategy.R_over_C: 3>

This strategy is meant to mimic the ACE strategy R/C. The HLT strategy is run until the first lookahead is triggered (when :the number of nodes active is at least :any:`lookahead_next`). A full lookahead is then performed, and then the CR strategy is used.

.. py:attribute:: strategy.Cr
:value: <strategy.Cr: 4>

This strategy is meant to mimic the ACE strategy Cr. The Felsch strategy is run until at least :any:`f_defs` new nodes have been defined, then the HLT strategy is run until at least :any:`hlt_defs` divided by :math:`N` nodes have been defined, where :math:`N` is the sum of the lengths of the words in the presentation and generating pairs. Then the Felsch strategy is run.

.. py:attribute:: strategy.Rc
:value: <strategy.Rc: 5>

This strategy is meant to mimic the ACE strategy Rc. The HLT strategy is run until at least :any:`hlt_defs` divided by :math:`N` new nodes have been defined (where :math:`N` is the sum of the lengths of the words in the presentation and generating pairs) the Felsch strategy is then run until at least :any:`f_defs` new nodes are defined, and then the HLT strategy is run.


Expand Down Expand Up @@ -126,7 +126,7 @@ The valid values are :

.. py:attribute:: lookahead_extent.partial
:value: <lookahead_extent.partial: 1>

Perform a partial lookahead starting from the current node in the word graph. Partial lookaheads are sometimes faster but may not detect as many coincidences as a full lookahead.
)pbdoc")
.value("full", ToddCoxeterImpl_::options::lookahead_extent::full)
Expand Down Expand Up @@ -235,12 +235,12 @@ The valid values are:

.. py:attribute:: def_version.one
:value: <def_version.one: 0>

Version 1 definition processing.

.. py:attribute:: def_version.two
:value: <def_version.two: 1>

Version 2 definition processing.
)pbdoc")
.value("one",
Expand Down Expand Up @@ -1422,6 +1422,7 @@ settings are ignored.
too few nodes are killed.
:type stop_early: bool
)pbdoc");

thing.def("shrink_to_fit",
&ToddCoxeterImpl_::shrink_to_fit,
R"pbdoc(
Expand All @@ -1432,6 +1433,7 @@ triggers a full enumeration, and standardization, and removes from
:any:`word_graph` any dead nodes. If :any:`Runner.finished` returns ``False``,
then this function does nothing.
)pbdoc");

thing.def("standardize",
&ToddCoxeterImpl_::standardize,
py::arg("val"),
Expand All @@ -1457,6 +1459,160 @@ calling this function.
:rtype: bool

.. seealso:: :any:`word_graph.standardize` and :any:`current_spanning_tree`.
)pbdoc");

thing.def(
"complete",
[](ToddCoxeterImpl_ const& self) { return self.complete(); },
R"pbdoc(
:sig=(self: ToddCoxeter) -> float:

Returns the proportion of edges defined in the active part of the current word
graph.

This function returns the proportion (as a float) of the edges defined. This
value is :any:`number_of_edges_active` divided by :any:`number_of_nodes_active`
multiplied by :any:`WordGraph.out_degree` applied to :any:`current_word_graph`.

:returns:
The proportion of edges defined in the active part of
:any:`current_word_graph`.
:rtype: float

.. doctest:: Python

>>> from libsemigroups_pybind11 import (Presentation, presentation, ToddCoxeter,
... congruence_kind)
>>> from datetime import timedelta
>>> p = Presentation("bcd")
>>> p.contains_empty_word(True)
<monoid presentation with 3 letters, 0 rules, and length 0>
>>> presentation.add_rule(p, "bb", "")
>>> presentation.add_rule(p, "cd", "")
>>> presentation.add_rule(p, "ccc", "")
>>> presentation.add_rule(p, "bcbcbcbcbcbcbc", "")
>>> presentation.add_rule(p, "bcbdbcbdbcbdbc", "")
>>> tc = ToddCoxeter(congruence_kind.twosided, p)
>>> tc.run()
>>> tc.complete()
1.0
)pbdoc");

thing.def("number_of_edges_active",
&ToddCoxeterImpl_::number_of_edges_active,
R"pbdoc(
:sig=(self: ToddCoxeter) -> int:

Return the number of edges in the active part of the current word graph.

This function returns the number of edges in the active part of the
:any:`current_word_graph`. Recall that :any:`current_word_graph` can grow and
shrink drastically during a congruence enumeration. As such to avoid
unnecessary memory allocations, where possible, the nodes in the
:any:`current_word_graph` are "recycled" leading to the situation where some of
the nodes in :any:`current_word_graph` are "active" and others are "inactive".
In other words, the "active" nodes correspond to the part of the word graph
that actually represents the classes of the congruence we are trying to
enumerate; and the "inactive" nodes are only there to be "recycled" into
"active" nodes if they are required later on.

:returns: The number of edges that are incident to active nodes.
:rtype: int

.. doctest:: Python

>>> from libsemigroups_pybind11 import (Presentation, presentation, ToddCoxeter,
... congruence_kind)
>>> from datetime import timedelta
>>> p = Presentation("bcd")
>>> p.contains_empty_word(True)
<monoid presentation with 3 letters, 0 rules, and length 0>
>>> presentation.add_rule(p, "bb", "")
>>> presentation.add_rule(p, "cd", "")
>>> presentation.add_rule(p, "ccc", "")
>>> presentation.add_rule(p, "bcbcbc", "")
>>> presentation.add_rule(p, "bcbdbcbd", "")
>>> tc = ToddCoxeter(congruence_kind.twosided, p)
>>> tc.number_of_classes()
12
>>> tc.number_of_edges_active()
36
)pbdoc");

thing.def("number_of_nodes_active",
&ToddCoxeterImpl_::number_of_nodes_active,
R"pbdoc(
:sig=(self: ToddCoxeter) -> int:

Return the number of nodes in the active part of the current word graph.

This function returns the number of nodes in the active part of the
:any:`current_word_graph`. Recall that :any:`current_word_graph` can grow and
shrink drastically during a congruence enumeration. As such to avoid
unnecessary memory allocations, where possible, the nodes in the
:any:`current_word_graph` are "recycled" leading to the situation where some of
the nodes in :any:`current_word_graph` are "active" and others are "inactive".
In other words, the "active" nodes correspond to the part of the word graph
that actually represents the classes of the congruence we are trying to
enumerate; and the "inactive" nodes are only there to be "recycled" into
"active" nodes if they are required later on.

:returns: The number of nodes that are active.
:rtype: int

.. doctest:: Python

>>> from libsemigroups_pybind11 import (Presentation, presentation, ToddCoxeter,
... congruence_kind)
>>> from datetime import timedelta
>>> p = Presentation("bcd")
>>> p.contains_empty_word(True)
<monoid presentation with 3 letters, 0 rules, and length 0>
>>> presentation.add_rule(p, "bb", "")
>>> presentation.add_rule(p, "cd", "")
>>> presentation.add_rule(p, "ccc", "")
>>> presentation.add_rule(p, "bcbcbc", "")
>>> presentation.add_rule(p, "bcbdbcbd", "")
>>> tc = ToddCoxeter(congruence_kind.twosided, p)
>>> tc.number_of_classes()
12
>>> tc.number_of_nodes_active()
12
)pbdoc");

thing.def("number_of_large_collapses",
&ToddCoxeterImpl_::number_of_large_collapses,
R"pbdoc(
:sig=(self: ToddCoxeter) -> int:

Return the number of large collapses that have occurred.

This function returns the number of "large" collapses that have occurred in the
graph during any run of a :any:`ToddCoxeter` instance. What qualifies as a "large"
collapse can be specified using :any:`large_collapse`.

:returns: The number of large collapses.
:rtype: int

.. doctest:: Python

>>> from libsemigroups_pybind11 import (Presentation, presentation, ToddCoxeter,
... congruence_kind)
>>> from datetime import timedelta
>>> p = Presentation("bcd")
>>> p.contains_empty_word(True)
<monoid presentation with 3 letters, 0 rules, and length 0>
>>> presentation.add_rule(p, "bb", "")
>>> presentation.add_rule(p, "cd", "")
>>> presentation.add_rule(p, "ccc", "")
>>> presentation.add_rule(p, "bcbcbcbcbcbcbc", "")
>>> presentation.add_rule(p, "bcbdbcbdbcbdbcbdbcbdbcbdbcbdbcbd", "")
>>> tc = ToddCoxeter(congruence_kind.twosided, p)
>>> tc.run_for(timedelta(milliseconds=10))
>>> tc.finished()
False
>>> tc.number_of_large_collapses()
0
)pbdoc");
} // init_todd_coxeter

Expand Down
14 changes: 13 additions & 1 deletion src/todd-coxeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ words. This function triggers no congruence enumeration.)pbdoc"sv});

////////////////////////////////////////////////////////////////////////

if constexpr (std::is_same_v<Word, word_type>) {
thing.def("presentation_no_checks",
[](ToddCoxeter_& self, Presentation<Word> const& p) {
self.presentation_no_checks(p);
});
} else {
thing.def("presentation_no_checks",
[](ToddCoxeter_& self, Presentation<Word> const& p) {
self.presentation_no_checks(to<Presentation<word_type>>(p));
});
}

thing.def(py::init<congruence_kind, ToddCoxeter_ const&>(),
py::arg("knd"),
py::arg("tc"),
Expand Down Expand Up @@ -620,7 +632,7 @@ Pro).
:param tc: the :any:`ToddCoxeter` instance.
:type tc: ToddCoxeter)pbdoc");
} // bind_todd_coxeter
} // namespace
} // namespace

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