libsemigroups  v3.0.0
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
knuth-bendix-impl.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2019-2025 James D. Mitchell + Joseph Edwards
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17//
18
19// This file contains a class KnuthBendixImpl which implements the Knuth-Bendix
20// algorithm for finitely presented monoids.
21
22// TODO(1)
23// * noexcept
24// * separate rule container from Rules
25// * nodiscard
26
27#ifndef LIBSEMIGROUPS_DETAIL_KNUTH_BENDIX_IMPL_HPP_
28#define LIBSEMIGROUPS_DETAIL_KNUTH_BENDIX_IMPL_HPP_
29
30#include <algorithm> // for max, min, copy
31#include <atomic> // for atomic_bool
32#include <cctype> // for isprint
33#include <chrono> // for duration_cast
34#include <cmath> // for pow
35#include <cstddef> // for size_t
36#include <cstdint> // for int64_t, uint32_t
37#include <iterator> // for back_inserter
38#include <memory> // for allocator, uni...
39#include <ostream> // for ostream
40#include <string> // for basic_string
41#include <string_view> // for basic_string_view
42#include <type_traits> // for is_same_v
43#include <unordered_map> // for unordered_map
44#include <utility> // for move, make_pair
45#include <vector> // for vector
46
47#include "libsemigroups/constants.hpp" // for POSITIVE_INFINITY
48#include "libsemigroups/debug.hpp" // for LIBSEMIGROUPS_...
49#include "libsemigroups/obvinf.hpp" // for is_obviously_infinite
50#include "libsemigroups/order.hpp" // for ShortLexCompare
51#include "libsemigroups/presentation.hpp" // for operator!=
52#include "libsemigroups/ranges.hpp" // for count, iterato...
53#include "libsemigroups/types.hpp" // for congruence_kind
54#include "libsemigroups/word-graph.hpp" // for WordGraph, to_...
55#include "libsemigroups/word-range.hpp" // for to_human_reada...
56
57#include "cong-common-class.hpp" // for CongruenceInte...
58#include "fmt.hpp" // for format, print
59#include "report.hpp" // for report_no_prefix
60#include "rewriters.hpp" // for Rule, internal...
61#include "string.hpp" // for group_digits
62#include "timer.hpp" // for string_time
63
64namespace libsemigroups {
65 // Forward declarations
66 namespace detail {
67 template <typename KnuthBendix_>
68 class KBE;
76
91
107
118
132
133 template <typename Rewriter = detail::RewriteTrie,
134 typename ReductionOrder = ShortLexCompare>
135 class KnuthBendixImpl : public CongruenceCommon {
136 // defined in detail/kbe.hpp
137 friend class ::libsemigroups::detail::KBE<KnuthBendixImpl>;
138
140 // KnuthBendixImpl - nested subclasses - private
142
143 // Overlap measures
144 struct OverlapMeasure {
145 virtual size_t
146 operator()(detail::Rule const*,
147 detail::Rule const* examples,
148 detail::internal_string_type::const_iterator const&)
149 = 0;
150 virtual ~OverlapMeasure() {}
151 };
152
153 struct ABC;
154 struct AB_BC;
155 struct MAX_AB_BC;
156
157 public:
159 // Interface requirements - native-types
161
162 using rule_type = std::pair<std::string, std::string>;
163 using native_word_type = std::string;
164 using rewriter_type = Rewriter;
165
167 // KnuthBendixImpl - types - public
169
170 struct options {
171 enum class overlap { ABC = 0, AB_BC = 1, MAX_AB_BC = 2 };
172 };
173
174 private:
175 struct Settings {
176 Settings() noexcept;
177 Settings& init() noexcept;
178
179 Settings(Settings const&) noexcept = default;
180 Settings(Settings&&) noexcept = default;
181 Settings& operator=(Settings const&) noexcept = default;
182 Settings& operator=(Settings&&) noexcept = default;
183
184 size_t max_pending_rules;
185 size_t check_confluence_interval;
186 size_t max_overlap;
187 size_t max_rules;
188 typename options::overlap overlap_policy;
189 };
190
191 struct Stats {
192 Stats() noexcept;
193 Stats& init() noexcept;
194
195 Stats(Stats const&) noexcept = default;
196 Stats(Stats&&) noexcept = default;
197 Stats& operator=(Stats const&) noexcept = default;
198 Stats& operator=(Stats&&) noexcept = default;
199
200 size_t prev_active_rules;
201 size_t prev_inactive_rules;
202 size_t prev_total_rules;
203 };
204
206 // KnuthBendixImpl - data - private
208
209 bool _gen_pairs_initted;
210 WordGraph<uint32_t> _gilman_graph;
211 std::vector<std::string> _gilman_graph_node_labels;
212 bool _internal_is_same_as_external;
213 std::unique_ptr<OverlapMeasure> _overlap_measure;
214 Presentation<std::string> _presentation;
215 mutable Rewriter _rewriter;
216 Settings _settings;
217 mutable Stats _stats;
218 mutable std::string _tmp_element1;
219
220 public:
222 // KnuthBendixImpl - constructors and destructor - public
224
225 KnuthBendixImpl();
226 KnuthBendixImpl& init();
227 KnuthBendixImpl(KnuthBendixImpl const& that);
228
229 KnuthBendixImpl(KnuthBendixImpl&&);
230
231 KnuthBendixImpl& operator=(KnuthBendixImpl const&);
232
233 KnuthBendixImpl& operator=(KnuthBendixImpl&&);
234
235 ~KnuthBendixImpl();
236
237 KnuthBendixImpl(congruence_kind knd, Presentation<std::string> const& p);
238
239 KnuthBendixImpl& init(congruence_kind knd,
240 Presentation<std::string> const& p);
241
242 KnuthBendixImpl(congruence_kind knd, Presentation<std::string>&& p);
243
244 KnuthBendixImpl& init(congruence_kind knd, Presentation<std::string>&& p);
245
246 // TODO(1) construct/init from kind and KnuthBendixImpl const&, for
247 // consistency with ToddCoxeterImpl
248
249 private:
250 void init_from_generating_pairs();
251 void init_from_internal_presentation();
252
254 // KnuthBendixImpl - interface requirements - add_generating_pair
256
257 public:
258 // NOTE THAT this is not the same as in ToddCoxeterImpl, because the
259 // generating pairs contained in CongruenceCommon are word_types, and
260 // so we don't require any conversion here (since chars can be converted
261 // implicitly to letter_types)
262 template <typename Iterator1,
263 typename Iterator2,
264 typename Iterator3,
265 typename Iterator4>
266 KnuthBendixImpl& add_generating_pair_no_checks(Iterator1 first1,
267 Iterator2 last1,
268 Iterator3 first2,
269 Iterator4 last2) {
270 LIBSEMIGROUPS_ASSERT(!started());
271 return CongruenceCommon::add_internal_generating_pair_no_checks<
272 KnuthBendixImpl>(first1, last1, first2, last2);
273 }
274
275 template <typename Iterator1,
276 typename Iterator2,
277 typename Iterator3,
278 typename Iterator4>
279 KnuthBendixImpl& add_generating_pair(Iterator1 first1,
280 Iterator2 last1,
281 Iterator3 first2,
282 Iterator4 last2) {
283 LIBSEMIGROUPS_ASSERT(!started());
284 return CongruenceCommon::add_generating_pair<KnuthBendixImpl>(
285 first1, last1, first2, last2);
286 }
287
289 // KnuthBendixImpl - interface requirements - number_of_classes
291
310 [[nodiscard]] uint64_t number_of_classes();
311
313 // KnuthBendixImpl - interface requirements - contains
315
334 template <typename Iterator1,
335 typename Iterator2,
336 typename Iterator3,
337 typename Iterator4>
338 [[nodiscard]] tril currently_contains_no_checks(Iterator1 first1,
339 Iterator2 last1,
340 Iterator3 first2,
341 Iterator4 last2) const;
342
343 // Documented in KnuthBendix (because it appears there because we call
344 // CongruenceCommon::currently_contains directly so that bounds checks are
345 // done in KnuthBendix)
346 template <typename Iterator1,
347 typename Iterator2,
348 typename Iterator3,
349 typename Iterator4>
350 [[nodiscard]] tril currently_contains(Iterator1 first1,
351 Iterator2 last1,
352 Iterator3 first2,
353 Iterator4 last2) const {
354 return CongruenceCommon::currently_contains<KnuthBendixImpl>(
355 first1, last1, first2, last2);
356 }
357
374 template <typename Iterator1,
375 typename Iterator2,
376 typename Iterator3,
377 typename Iterator4>
378 [[nodiscard]] bool contains_no_checks(Iterator1 first1,
379 Iterator2 last1,
380 Iterator3 first2,
381 Iterator4 last2) {
382 return CongruenceCommon::contains_no_checks<KnuthBendixImpl>(
383 first1, last1, first2, last2);
384 }
385
386 // Documented in KnuthBendix (because it appears there because we call
387 // CongruenceCommon::contains directly so that bounds checks are
388 // done in KnuthBendix)
389 template <typename Iterator1,
390 typename Iterator2,
391 typename Iterator3,
392 typename Iterator4>
393 [[nodiscard]] bool contains(Iterator1 first1,
394 Iterator2 last1,
395 Iterator3 first2,
396 Iterator4 last2) {
397 return CongruenceCommon::contains<KnuthBendixImpl>(
398 first1, last1, first2, last2);
399 }
400
402 // KnuthBendixImpl - interface requirements - reduce
404
425 template <typename OutputIterator,
426 typename InputIterator1,
427 typename InputIterator2>
428 OutputIterator reduce_no_run_no_checks(OutputIterator d_first,
429 InputIterator1 first,
430 InputIterator2 last) const;
431
432 // Documented in KnuthBendix (because it appears there because we call
433 // CongruenceCommon::reduce_no_run directly so that bounds checks are
434 // done in KnuthBendix)
435 template <typename OutputIterator,
436 typename InputIterator1,
437 typename InputIterator2>
438 OutputIterator reduce_no_run(OutputIterator d_first,
439 InputIterator1 first,
440 InputIterator2 last) const {
441 return CongruenceCommon::reduce_no_run<KnuthBendixImpl>(
442 d_first, first, last);
443 }
444
466 template <typename OutputIterator,
467 typename InputIterator1,
468 typename InputIterator2>
469 OutputIterator reduce_no_checks(OutputIterator d_first,
470 InputIterator1 first,
471 InputIterator2 last) {
472 return CongruenceCommon::reduce_no_checks<KnuthBendixImpl>(
473 d_first, first, last);
474 }
475
476 // Documented in KnuthBendix (because it appears there because we call
477 // CongruenceCommon::reduce directly so that bounds checks are
478 // done in KnuthBendix)
479 template <typename OutputIterator,
480 typename InputIterator1,
481 typename InputIterator2>
482 OutputIterator reduce(OutputIterator d_first,
483 InputIterator1 first,
484 InputIterator2 last) {
485 return CongruenceCommon::reduce<KnuthBendixImpl>(d_first, first, last);
486 }
487
488 // TODO(1) implement reduce_inplace x4 if possible.
489
491 // KnuthBendixImpl - setters for optional parameters - public
493
514 KnuthBendixImpl& max_pending_rules(size_t val) {
515 _settings.max_pending_rules = val;
516 return *this;
517 }
518
538 [[nodiscard]] size_t max_pending_rules() const noexcept {
539 return _settings.max_pending_rules;
540 }
541
564 KnuthBendixImpl& check_confluence_interval(size_t val) {
565 _settings.check_confluence_interval = val;
566 return *this;
567 }
568
588 [[nodiscard]] size_t check_confluence_interval() const noexcept {
589 return _settings.check_confluence_interval;
590 }
591
613 KnuthBendixImpl& max_overlap(size_t val) {
614 _settings.max_overlap = val;
615 return *this;
616 }
617
636 [[nodiscard]] size_t max_overlap() const noexcept {
637 return _settings.max_overlap;
638 }
639
660 KnuthBendixImpl& max_rules(size_t val) {
661 _settings.max_rules = val;
662 return *this;
663 }
664
685 [[nodiscard]] size_t max_rules() const noexcept {
686 return _settings.max_rules;
687 }
688
705 KnuthBendixImpl& overlap_policy(typename options::overlap val);
706
724 [[nodiscard]] typename options::overlap overlap_policy() const noexcept {
725 return _settings.overlap_policy;
726 }
727
729 // KnuthBendixImpl - member functions for rules and rewriting - public
731
732 // TODO(1) remove
733 template <typename Iterator1, typename Iterator2>
734 void throw_if_letter_not_in_alphabet(Iterator1 first,
735 Iterator2 last) const {
736 internal_presentation().throw_if_letter_not_in_alphabet(first, last);
737 }
738
739 [[nodiscard]] Presentation<std::string> const&
740 internal_presentation() const noexcept {
741 return _presentation;
742 }
743
759 [[nodiscard]] size_t number_of_active_rules() const noexcept;
760
777 [[nodiscard]] size_t number_of_inactive_rules() const noexcept {
778 return _rewriter.number_of_inactive_rules();
779 }
780
799 [[nodiscard]] size_t total_rules() const noexcept {
800 return _rewriter.stats().total_rules;
801 }
802
803 // Documented in KnuthBendix
804 // TODO(1) should be const
805 // TODO(1) add note about empty active rules after, or better discuss that
806 // there are three kinds of rules in the system: active, inactive, and
807 // pending.
808 [[nodiscard]] auto active_rules();
809
810 private:
811 // TODO(1) remove this ...
812 void rewrite_inplace(std::string& w);
813
814 // TODO(1) remove this ...
815 [[nodiscard]] std::string rewrite(std::string w) {
816 rewrite_inplace(w);
817 return w;
818 }
819
820 public:
822 // KnuthBendixImpl - main member functions - public
824
833 [[nodiscard]] bool confluent() const;
834
845 [[nodiscard]] bool confluent_known() const noexcept;
846
869 WordGraph<uint32_t> const& gilman_graph();
870
871 // Documented in KnuthBendix
872 [[nodiscard]] std::vector<std::string> const& gilman_graph_node_labels() {
873 gilman_graph(); // to ensure that gilman_graph is initialised
874 return _gilman_graph_node_labels;
875 }
876
877 private:
879 // KnuthBendixImpl - private member functions
881
882 void report_presentation() const;
883 void report_before_run();
884 void report_progress_from_thread(std::atomic_bool const&);
885 void report_after_run();
886
887 void stats_check_point();
888
889 [[nodiscard]] static detail::internal_char_type
890 uint_to_internal_char(size_t a);
891 [[nodiscard]] static size_t
892 internal_char_to_uint(detail::internal_char_type c);
893
894 [[nodiscard]] static detail::internal_string_type
895 uint_to_internal_string(size_t i);
896
897 [[nodiscard]] static word_type
898 internal_string_to_word(detail::internal_string_type const& s);
899
900 [[nodiscard]] detail::internal_char_type
901 external_to_internal_char(detail::external_char_type c) const;
902 [[nodiscard]] detail::external_char_type
903 internal_to_external_char(detail::internal_char_type a) const;
904
905 void external_to_internal_string(detail::external_string_type& w) const;
906 void internal_to_external_string(detail::internal_string_type& w) const;
907
908 void add_octo(detail::external_string_type& w) const;
909 void rm_octo(detail::external_string_type& w) const;
910
911 void add_rule_impl(std::string const& p, std::string const& q);
912
913 void overlap(detail::Rule const* u, detail::Rule const* v);
914
915 [[nodiscard]] size_t max_active_word_length() const {
916 return _rewriter.max_active_word_length();
917 }
918
919 void run_real(std::atomic_bool&);
920 [[nodiscard]] bool stop_running() const;
921
923 // Runner - pure virtual member functions - private
925
926 void run_impl() override;
927 bool finished_impl() const override;
928 }; // class KnuthBendixImpl
929 } // namespace detail
930
932 // global functions - to_human_readable_repr
934
935 // TODO(1) for consistency (with ToddCoxeter), the next two functions should
936 // really be in knuth-bendix.hpp.
937
952#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
953 template <typename Word, typename Rewriter, typename ReductionOrder>
956 KnuthBendix<Word, Rewriter, ReductionOrder> const& kb);
957#else
958 template <typename Rewriter, typename ReductionOrder>
961 detail::KnuthBendixImpl<Rewriter, ReductionOrder> const& kb);
962#endif
963
981 // TODO(1) preferably kb would be a const&
982#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
983 template <typename Word, typename Rewriter, typename ReductionOrder>
985 to_human_readable_repr(KnuthBendix<Word, Rewriter, ReductionOrder>& kb);
986#else
987 template <typename Rewriter, typename ReductionOrder>
989 to_human_readable_repr(detail::KnuthBendixImpl<Rewriter, ReductionOrder>& kb);
990#endif
991
993 // TODO(1) kb should be const
994 template <typename Result, typename Rewriter, typename ReductionOrder>
995 auto to(detail::KnuthBendixImpl<Rewriter, ReductionOrder>& kb)
996 -> std::enable_if_t<
997 std::is_same_v<Presentation<typename Result::word_type>, Result>,
998 Result>;
999
1000} // namespace libsemigroups
1001
1002#include "knuth-bendix-impl.tpp"
1003
1004#endif // LIBSEMIGROUPS_DETAIL_KNUTH_BENDIX_IMPL_HPP_
For an implementation of presentations for semigroups or monoids.
Definition presentation.hpp:102
bool started() const noexcept
Check if run has been called at least once before.
Definition runner.hpp:578
Class for representing word graphs.
Definition word-graph.hpp:82
For a rewriting rule.
Definition rewriters.hpp:83
std::string to_human_readable_repr(AhoCorasick const &ac)
Return a string representation.
std::ostringstream & operator<<(std::ostringstream &os, BMat8 const &x)
Insertion operator.
void add_generating_pair(Thing &thing, typename Thing::native_word_type const &u, typename Thing::native_word_type const &v)
Helper for adding a generating pair of words.
Definition cong-common-helpers.hpp:200
size_t number_of_active_rules() const noexcept
Return the current number of active rules in the KnuthBendix instance.
size_t number_of_inactive_rules() const noexcept
Definition knuth-bendix-impl.hpp:777
bool confluent_known() const noexcept
Check if the current system knows the state of confluence of the current rules.
bool confluent() const
Check confluence of the current rules.
size_t total_rules() const noexcept
Return the number of rules that KnuthBendix has created.
Definition knuth-bendix-impl.hpp:799
OutputIterator reduce_no_checks(OutputIterator d_first, InputIterator1 first, InputIterator2 last)
Reduce a word with no checks.
Definition knuth-bendix-impl.hpp:469
uint64_t number_of_classes()
Compute the number of classes in the congruence.
bool contains_no_checks(Iterator1 first1, Iterator2 last1, Iterator3 first2, Iterator4 last2)
Check containment of a pair of words via iterators.
Definition knuth-bendix-impl.hpp:378
tril currently_contains_no_checks(Iterator1 first1, Iterator2 last1, Iterator3 first2, Iterator4 last2) const
Check containment of a pair of words via iterators.
OutputIterator reduce_no_run_no_checks(OutputIterator d_first, InputIterator1 first, InputIterator2 last) const
Reduce a word with no run and no checks.
KnuthBendixImpl & check_confluence_interval(size_t val)
Definition knuth-bendix-impl.hpp:564
KnuthBendixImpl & max_pending_rules(size_t val)
Definition knuth-bendix-impl.hpp:514
KnuthBendixImpl & max_overlap(size_t val)
Definition knuth-bendix-impl.hpp:613
KnuthBendixImpl & max_rules(size_t val)
Definition knuth-bendix-impl.hpp:660
KnuthBendixImpl & overlap_policy(typename options::overlap val)
Presentation(Presentation< Word > const &) -> Presentation< Word >
Deduction guide.
std::string internal_string_type
Definition rewriters.hpp:58
char external_char_type
Definition rewriters.hpp:63
std::string external_string_type
Definition rewriters.hpp:53
char internal_char_type
Definition rewriters.hpp:68
auto to(detail::KnuthBendixImpl< Rewriter, ReductionOrder > &kb) -> std::enable_if_t< std::is_same_v< Presentation< typename Result::word_type >, Result >, Result >
No doc.
std::vector< letter_type > word_type
Type for a word over the generators of a semigroup.
Definition types.hpp:101
tril
Enum to indicate true, false or not currently knowable.
Definition types.hpp:56
congruence_kind
Enum to indicate the sided-ness of a congruence.
Definition types.hpp:69
Namespace for everything in the libsemigroups library.
Definition action.hpp:44