libsemigroups  v3.3.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/adapters.hpp" // for Hash
48#include "libsemigroups/constants.hpp" // for POSITIVE_INFINITY
49#include "libsemigroups/debug.hpp" // for LIBSEMIGROUPS_...
50#include "libsemigroups/obvinf.hpp" // for is_obviously_infinite
51#include "libsemigroups/order.hpp" // for ShortLexCompare
52#include "libsemigroups/paths-count.hpp" // for paths::count
53#include "libsemigroups/presentation.hpp" // for operator!=
54#include "libsemigroups/ranges.hpp" // for count, iterato...
55#include "libsemigroups/types.hpp" // for congruence_kind
56#include "libsemigroups/word-graph-helpers.hpp" // for word_graph
57#include "libsemigroups/word-graph.hpp" // for WordGraph, to_...
58#include "libsemigroups/word-range.hpp" // for to_human_reada...
59
60#include "cong-common-class.hpp" // for CongruenceInte...
61#include "fmt.hpp" // for format, print
62#include "report.hpp" // for report_no_prefix
63#include "rewriters.hpp" // for Rule, internal...
64#include "string.hpp" // for group_digits
65#include "timer.hpp" // for string_time
66
67namespace libsemigroups {
68 // Forward declarations
69 namespace detail {
70 template <typename KnuthBendix_>
71 class KBE;
79
94
110
121
135
136 template <typename Rewriter = detail::RewriteTrie,
137 typename ReductionOrder = ShortLexCompare>
138 class KnuthBendixImpl : public CongruenceCommon {
139 public:
141 // Aliases
143
144 using native_word_type = typename Rewriter::native_word_type;
145 using rule_type = std::pair<native_word_type, native_word_type>;
146 using rewriter_type = Rewriter;
147
149 // Nested classes - public
151
152 struct options {
153 enum class overlap { ABC = 0, AB_BC = 1, MAX_AB_BC = 2 };
154 };
155
156 private:
158 // Nested classes - private
160
161 // Overlap measures
162 struct OverlapMeasure {
163 virtual size_t
164 operator()(detail::Rule const*,
165 detail::Rule const* examples,
166 typename native_word_type::const_iterator const&)
167 = 0;
168 virtual ~OverlapMeasure() {}
169 };
170
171 struct ABC;
172 struct AB_BC;
173 struct MAX_AB_BC;
174
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<native_word_type> _gilman_graph_node_labels;
212 std::unique_ptr<OverlapMeasure> _overlap_measure;
213 Presentation<native_word_type> _presentation;
214 mutable Rewriter _rewriter;
215 Settings _settings;
216 mutable Stats _stats;
217 mutable native_word_type _tmp_element1;
218
219 public:
221 // KnuthBendixImpl - constructors and destructor - public
223
224 KnuthBendixImpl();
225 KnuthBendixImpl& init();
226 KnuthBendixImpl(KnuthBendixImpl const& that);
227
228 KnuthBendixImpl(KnuthBendixImpl&&);
229
230 KnuthBendixImpl& operator=(KnuthBendixImpl const&);
231
232 KnuthBendixImpl& operator=(KnuthBendixImpl&&);
233
234 ~KnuthBendixImpl();
235
236 KnuthBendixImpl(congruence_kind knd,
237 Presentation<native_word_type> const& p);
238
239 KnuthBendixImpl& init(congruence_kind knd,
240 Presentation<native_word_type> const& p);
241
242 KnuthBendixImpl(congruence_kind knd, Presentation<native_word_type>&& p);
243
244 KnuthBendixImpl& init(congruence_kind knd,
245 Presentation<native_word_type>&& p);
246
247 // TODO(1) construct/init from kind and KnuthBendixImpl const&, for
248 // consistency with ToddCoxeterImpl
249
250 private:
251 void init_from_generating_pairs();
252 void init_from_internal_presentation();
253
255 // KnuthBendixImpl - interface requirements - add_generating_pair
257
258 public:
259 // NOTE THAT this is not the same as in ToddCoxeterImpl, because the
260 // generating pairs contained in CongruenceCommon are word_types, and
261 // so we don't require any conversion here (since chars can be converted
262 // implicitly to letter_types)
263 template <typename Iterator1,
264 typename Iterator2,
265 typename Iterator3,
266 typename Iterator4>
267 KnuthBendixImpl& add_generating_pair_no_checks(Iterator1 first1,
268 Iterator2 last1,
269 Iterator3 first2,
270 Iterator4 last2) {
271 LIBSEMIGROUPS_ASSERT(!started());
272 return CongruenceCommon::add_internal_generating_pair_no_checks<
273 KnuthBendixImpl>(first1, last1, first2, last2);
274 }
275
276 template <typename Iterator1,
277 typename Iterator2,
278 typename Iterator3,
279 typename Iterator4>
280 KnuthBendixImpl& add_generating_pair(Iterator1 first1,
281 Iterator2 last1,
282 Iterator3 first2,
283 Iterator4 last2) {
284 LIBSEMIGROUPS_ASSERT(!started());
285 return CongruenceCommon::add_generating_pair<KnuthBendixImpl>(
286 first1, last1, first2, last2);
287 }
288
290 // KnuthBendixImpl - interface requirements - number_of_classes
292
311 [[nodiscard]] uint64_t number_of_classes();
312
314 // KnuthBendixImpl - interface requirements - contains
316
335 template <typename Iterator1,
336 typename Iterator2,
337 typename Iterator3,
338 typename Iterator4>
339 [[nodiscard]] tril currently_contains_no_checks(Iterator1 first1,
340 Iterator2 last1,
341 Iterator3 first2,
342 Iterator4 last2) const;
343
344 // Documented in KnuthBendix (because it appears there because we call
345 // CongruenceCommon::currently_contains directly so that bounds checks are
346 // done in KnuthBendix)
347 template <typename Iterator1,
348 typename Iterator2,
349 typename Iterator3,
350 typename Iterator4>
351 [[nodiscard]] tril currently_contains(Iterator1 first1,
352 Iterator2 last1,
353 Iterator3 first2,
354 Iterator4 last2) const {
355 return CongruenceCommon::currently_contains<KnuthBendixImpl>(
356 first1, last1, first2, last2);
357 }
358
375 template <typename Iterator1,
376 typename Iterator2,
377 typename Iterator3,
378 typename Iterator4>
379 [[nodiscard]] bool contains_no_checks(Iterator1 first1,
380 Iterator2 last1,
381 Iterator3 first2,
382 Iterator4 last2) {
383 return CongruenceCommon::contains_no_checks<KnuthBendixImpl>(
384 first1, last1, first2, last2);
385 }
386
387 // Documented in KnuthBendix (because it appears there because we call
388 // CongruenceCommon::contains directly so that bounds checks are
389 // done in KnuthBendix)
390 template <typename Iterator1,
391 typename Iterator2,
392 typename Iterator3,
393 typename Iterator4>
394 [[nodiscard]] bool contains(Iterator1 first1,
395 Iterator2 last1,
396 Iterator3 first2,
397 Iterator4 last2) {
398 return CongruenceCommon::contains<KnuthBendixImpl>(
399 first1, last1, first2, last2);
400 }
401
403 // KnuthBendixImpl - interface requirements - reduce
405
406 template <typename OutputIterator,
407 typename InputIterator1,
408 typename InputIterator2>
409 OutputIterator reduce_no_run_no_checks(OutputIterator d_first,
410 InputIterator1 first,
411 InputIterator2 last) const;
412
413 // Documented in KnuthBendix (because it appears there because we call
414 // CongruenceCommon::reduce_no_run directly so that bounds checks are
415 // done in KnuthBendix)
416 template <typename OutputIterator,
417 typename InputIterator1,
418 typename InputIterator2>
419 OutputIterator reduce_no_run(OutputIterator d_first,
420 InputIterator1 first,
421 InputIterator2 last) const {
422 return CongruenceCommon::reduce_no_run<KnuthBendixImpl>(
423 d_first, first, last);
424 }
425
426 template <typename OutputIterator,
427 typename InputIterator1,
428 typename InputIterator2>
429 OutputIterator reduce_no_checks(OutputIterator d_first,
430 InputIterator1 first,
431 InputIterator2 last) {
432 return CongruenceCommon::reduce_no_checks<KnuthBendixImpl>(
433 d_first, first, last);
434 }
435
436 // Documented in KnuthBendix (because it appears there because we call
437 // CongruenceCommon::reduce directly so that bounds checks are
438 // done in KnuthBendix)
439 template <typename OutputIterator,
440 typename InputIterator1,
441 typename InputIterator2>
442 OutputIterator reduce(OutputIterator d_first,
443 InputIterator1 first,
444 InputIterator2 last) {
445 return CongruenceCommon::reduce<KnuthBendixImpl>(d_first, first, last);
446 }
447
448 // TODO(1) implement reduce_inplace x4 if possible.
449
451 // KnuthBendixImpl - setters for optional parameters - public
453
474 KnuthBendixImpl& max_pending_rules(size_t val) {
475 _settings.max_pending_rules = val;
476 return *this;
477 }
478
498 [[nodiscard]] size_t max_pending_rules() const noexcept {
499 return _settings.max_pending_rules;
500 }
501
524 KnuthBendixImpl& check_confluence_interval(size_t val) {
525 _settings.check_confluence_interval = val;
526 return *this;
527 }
528
548 [[nodiscard]] size_t check_confluence_interval() const noexcept {
549 return _settings.check_confluence_interval;
550 }
551
573 KnuthBendixImpl& max_overlap(size_t val) {
574 _settings.max_overlap = val;
575 return *this;
576 }
577
596 [[nodiscard]] size_t max_overlap() const noexcept {
597 return _settings.max_overlap;
598 }
599
620 KnuthBendixImpl& max_rules(size_t val) {
621 _settings.max_rules = val;
622 return *this;
623 }
624
645 [[nodiscard]] size_t max_rules() const noexcept {
646 return _settings.max_rules;
647 }
648
665 KnuthBendixImpl& overlap_policy(typename options::overlap val);
666
684 [[nodiscard]] typename options::overlap overlap_policy() const noexcept {
685 return _settings.overlap_policy;
686 }
687
689 // KnuthBendixImpl - member functions for rules and rewriting - public
691
692 // TODO(1) remove
693 template <typename Iterator1, typename Iterator2>
694 void throw_if_letter_not_in_alphabet(Iterator1 first,
695 Iterator2 last) const {
696 internal_presentation().throw_if_letter_not_in_alphabet(first, last);
697 }
698
699 [[nodiscard]] Presentation<native_word_type> const&
700 internal_presentation() const noexcept {
701 return _presentation;
702 }
703
719 [[nodiscard]] size_t number_of_active_rules() const noexcept;
720
737 [[nodiscard]] size_t number_of_inactive_rules() const noexcept {
738 return _rewriter.number_of_inactive_rules();
739 }
740
761 [[nodiscard]] size_t number_of_pending_rules() const noexcept {
762 return _rewriter.number_of_pending_rules();
763 }
764
783 [[nodiscard]] size_t total_rules() const noexcept {
784 return _rewriter.stats().total_rules;
785 }
786
787 Rewriter& rewriter() noexcept {
788 return _rewriter;
789 }
790
791 // Documented in KnuthBendix
792 // TODO(1) should be const
793 // TODO(1) add note about empty active rules after, or better discuss that
794 // there are three kinds of rules in the system: active, inactive, and
795 // pending.
796 [[nodiscard]] auto active_rules();
797
816 KnuthBendixImpl& process_pending_rules() {
817 _rewriter.process_pending_rules();
818 return *this;
819 }
820
821 private:
822 // TODO(1) remove this ...
823 void rewrite_inplace(native_word_type& w);
824
825 // TODO(1) remove this ...
826 [[nodiscard]] native_word_type rewrite(native_word_type w) {
827 rewrite_inplace(w);
828 return w;
829 }
830
831 public:
833 // KnuthBendixImpl - main member functions - public
835
844 [[nodiscard]] bool confluent() const;
845
856 [[nodiscard]] bool confluent_known() const noexcept;
857
880 WordGraph<uint32_t> const& gilman_graph();
881
882 // Documented in KnuthBendix
883 [[nodiscard]] std::vector<native_word_type> const&
884 gilman_graph_node_labels() {
885 gilman_graph(); // to ensure that gilman_graph is initialised
886 return _gilman_graph_node_labels;
887 }
888
889 protected:
890 // run_impl is called by KnuthBendix
891 void run_impl() override;
892
893 private:
895 // KnuthBendixImpl - private member functions
897
898 void report_presentation() const;
899 void report_before_run();
900 void report_progress_from_thread(std::atomic_bool const&);
901 void report_after_run();
902
903 void stats_check_point();
904
905 void add_octo(native_word_type& w) const;
906 void rm_octo(native_word_type& w) const;
907
908 void add_rule_impl(native_word_type const& p, native_word_type const& q);
909
910 void overlap(detail::Rule const* u, detail::Rule const* v);
911
912 [[nodiscard]] size_t max_active_word_length() const {
913 return _rewriter.max_active_word_length();
914 }
915
916 void run_real(std::atomic_bool&);
917 [[nodiscard]] bool stop_running() const;
918
920 // Runner - pure virtual member functions - private
922
923 bool finished_impl() const override;
924 }; // class KnuthBendixImpl
925 } // namespace detail
926
928 // global functions - to_human_readable_repr
930
931 // TODO(1) for consistency (with ToddCoxeter), the next two functions should
932 // really be in knuth-bendix.hpp.
933
948#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
949 template <typename Word, typename Rewriter, typename ReductionOrder>
952 KnuthBendix<Word, Rewriter, ReductionOrder> const& kb);
953#else
954 template <typename Rewriter, typename ReductionOrder>
957 detail::KnuthBendixImpl<Rewriter, ReductionOrder> const& kb);
958#endif
959
977 // TODO(1) preferably kb would be a const&
978#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
979 template <typename Word, typename Rewriter, typename ReductionOrder>
981 to_human_readable_repr(KnuthBendix<Word, Rewriter, ReductionOrder>& kb);
982#else
983 template <typename Rewriter, typename ReductionOrder>
985 to_human_readable_repr(detail::KnuthBendixImpl<Rewriter, ReductionOrder>& kb);
986#endif
987
989 // TODO(1) kb should be const
990 template <typename Result, typename Rewriter, typename ReductionOrder>
991 auto to(detail::KnuthBendixImpl<Rewriter, ReductionOrder>& kb)
992 -> std::enable_if_t<
993 std::is_same_v<Presentation<typename Result::word_type>, Result>,
994 Result>;
995
996} // namespace libsemigroups
997
998#include "knuth-bendix-impl.tpp"
999
1000#endif // LIBSEMIGROUPS_DETAIL_KNUTH_BENDIX_IMPL_HPP_
For an implementation of presentations for semigroups or monoids.
Definition presentation.hpp:103
bool started() const noexcept
Check if run has been called at least once before.
Definition runner.hpp:626
Class for representing word graphs.
Definition word-graph.hpp:83
std::string to_human_readable_repr(Action< Element, Point, Func, Traits, LeftOrRight > const &action)
Return a human readable representation of an Action object.
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
Thing::native_word_type reduce(Thing const &thing, typename Thing::native_word_type const &w)
Reduce a word.
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:737
bool confluent_known() const noexcept
Check if the current system knows the state of confluence of the current rules.
size_t number_of_pending_rules() const noexcept
Return the number of pending rules.
Definition knuth-bendix-impl.hpp:761
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:783
KnuthBendixImpl & process_pending_rules()
Process any pending rules.
Definition knuth-bendix-impl.hpp:816
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:379
tril currently_contains_no_checks(Iterator1 first1, Iterator2 last1, Iterator3 first2, Iterator4 last2) const
Check containment of a pair of words via iterators.
KnuthBendixImpl & check_confluence_interval(size_t val)
Definition knuth-bendix-impl.hpp:524
KnuthBendixImpl & max_pending_rules(size_t val)
Definition knuth-bendix-impl.hpp:474
KnuthBendixImpl & max_overlap(size_t val)
Definition knuth-bendix-impl.hpp:573
KnuthBendixImpl & max_rules(size_t val)
Definition knuth-bendix-impl.hpp:620
KnuthBendixImpl & overlap_policy(typename options::overlap val)
Presentation(Presentation< Word > const &) -> Presentation< Word >
Deduction guide.
tril
Enum to indicate true, false or not currently knowable.
Definition types.hpp:54
congruence_kind
Enum to indicate the sided-ness of a congruence.
Definition types.hpp:67
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
auto to(detail::KnuthBendixImpl< Rewriter, ReductionOrder > &kb) -> std::enable_if_t< std::is_same_v< Presentation< typename Result::word_type >, Result >, Result >
No doc.