libsemigroups  v3.3.0
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
word-graph-helpers.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2025 Nadim Searight
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 helper functions for word graphs and word graph views
20
21#ifndef LIBSEMIGROUPS_WORD_GRAPH_HELPERS_HPP_
22#define LIBSEMIGROUPS_WORD_GRAPH_HELPERS_HPP_
23
24#include <algorithm> // for max, fill
25#include <initializer_list> // for initializer_list
26#include <iosfwd> // for ostream
27#include <iterator> // for empty
28#include <numeric> // for iota
29#include <queue> // for queue
30#include <random> // for mt19937, random_device
31#include <stack> // for stack
32#include <stddef.h> // for size_t
33#include <stdint.h> // for uint64_t, uint8_t
34#include <string> // for basic_string, allocator
35#include <tuple> // for tie
36#include <type_traits> // for enable_if_t, decay_t
37#include <unordered_map> // for unordered_map
38#include <unordered_set> // for unordered_set
39#include <utility> // for pair, move, make_pair
40#include <vector> // for vector, swap
41
42#include "libsemigroups/adapters.hpp" // for Hash
43#include "libsemigroups/config.hpp" // for LIBSEMIGROUPS_EIGEN_...
44#include "libsemigroups/constants.hpp" // for UNDEFINED
45#include "libsemigroups/debug.hpp" // for LIBSEMIGROUPS_ASSERT
46#include "libsemigroups/dot.hpp" // for Dot
47#include "libsemigroups/exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
48#include "libsemigroups/forest.hpp" // for Forest
49#include "libsemigroups/order.hpp" // for Order
50#include "libsemigroups/types.hpp" // for word_type, letter_type
51#include "libsemigroups/word-graph-view.hpp" // for WordGraphView
52#include "libsemigroups/word-graph.hpp" // for WordGraph
53
54#include "libsemigroups/detail/fmt.hpp" // for fmt::format
55#include "libsemigroups/detail/stl.hpp" // for HasLessEqual
56#include "libsemigroups/detail/string.hpp" // for group_digits
57#include "libsemigroups/detail/uf.hpp" // for Duf
58
59#ifdef LIBSEMIGROUPS_EIGEN_ENABLED
60#include "detail/eigen.hpp"
61#else
62#include "matrix.hpp"
63#endif
64
65namespace libsemigroups {
66
67 namespace v4 {
68
69 // TODO(v4) Turn this into a doxygen comment once it is moved out of the v4
70 // namespace
71 // \ingroup word_graph_group
72 //
73 // \brief Namespace containing helper functions for the \ref WordGraph
74 // class.
75 //
76 // Defined in `word-graph-helpers.hpp`.
77 //
78 // \brief This namespace contains helper functions for the \ref WordGraph
79 // class.
80 namespace word_graph {
81
83 // WordGraph - helper functions - in alphabetical order!!!
85
108 // TODO(1) add add_cycle with checks version.
109 template <typename Node, typename Iterator>
110 void add_cycle_no_checks(WordGraph<Node>& wg,
111 Iterator first,
112 Iterator last);
113
129 template <typename Node>
130 void add_cycle(WordGraph<Node>& wg, size_t N) {
131 size_t M = wg.number_of_nodes();
132 wg.add_nodes(N);
133 add_cycle_no_checks(wg, wg.cbegin_nodes() + M, wg.cend_nodes());
134 }
135
151 template <typename Node>
152 [[nodiscard]] auto adjacency_matrix(WordGraphView<Node> const& wg);
153
169 template <typename Node>
170 [[nodiscard]] auto adjacency_matrix(WordGraph<Node> const& wg) {
171 return adjacency_matrix(WordGraphView<Node>(wg));
172 }
173
184 template <typename Node>
185 [[nodiscard]] Dot dot(WordGraphView<Node> const& wg);
186
197 template <typename Node>
198 [[nodiscard]] Dot dot(WordGraph<Node> const& wg) {
199 return dot(WordGraphView<Node>(wg));
200 }
201
228 template <typename Node>
229 [[nodiscard]] bool equal_to_no_checks(WordGraph<Node> const& x,
230 WordGraph<Node> const& y,
231 Node first,
232 Node last) {
233 WordGraphView<Node> x_view = WordGraphView<Node>(x, first, last);
234 WordGraphView<Node> y_view = WordGraphView<Node>(y, first, last);
235 return x_view == y_view;
236 }
237
262 template <typename Node>
263 [[nodiscard]] bool equal_to(WordGraph<Node> const& x,
264 WordGraph<Node> const& y,
265 Node first,
266 Node last);
267
300 template <typename Node1, typename Node2, typename Iterator>
301 [[nodiscard]] Node1 follow_path(WordGraphView<Node1> const& wg,
302 Node2 source,
303 Iterator first,
304 Iterator last);
305
338 template <typename Node1, typename Node2, typename Iterator>
339 [[nodiscard]] Node1 follow_path(WordGraph<Node1> const& wg,
340 Node2 source,
341 Iterator first,
342 Iterator last) {
343 return follow_path(WordGraphView<Node1>(wg), source, first, last);
344 }
345
373 // TODO(2) example
374 // not noexcept because WordGraph::target isn't
375 template <typename Node1, typename Node2>
376 [[nodiscard]] Node1 follow_path(WordGraphView<Node1> const& wg,
377 Node2 from,
378 word_type const& path) {
379 static_assert(sizeof(Node2) <= sizeof(Node1));
380 return follow_path(wg, from, path.cbegin(), path.cend());
381 }
382
410 // TODO(2) example
411 // not noexcept because WordGraph::target isn't
412 template <typename Node1, typename Node2>
413 [[nodiscard]] Node1 follow_path(WordGraph<Node1> const& wg,
414 Node2 from,
415 word_type const& path) {
416 static_assert(sizeof(Node2) <= sizeof(Node1));
417 return follow_path(
418 WordGraphView<Node1>(wg), from, path.cbegin(), path.cend());
419 }
420
447 template <typename Node1, typename Node2, typename Iterator>
448 [[nodiscard]] Node1 follow_path_no_checks(WordGraphView<Node1> const& wg,
449 Node2 from,
450 Iterator first,
451 Iterator last) noexcept;
452
479 template <typename Node1, typename Node2, typename Iterator>
480 [[nodiscard]] Node1 follow_path_no_checks(WordGraph<Node1> const& wg,
481 Node2 from,
482 Iterator first,
483 Iterator last) noexcept {
484 return follow_path_no_checks(
485 WordGraphView<Node1>(wg), from, first, last);
486 }
487
514 template <typename Node1, typename Node2>
515 [[nodiscard]] Node1
516 follow_path_no_checks(WordGraphView<Node1> const& wg,
517 Node2 from,
518 word_type const& path) noexcept {
519 static_assert(sizeof(Node2) <= sizeof(Node1));
520 return follow_path_no_checks(wg, from, path.cbegin(), path.cend());
521 }
522
549 template <typename Node1, typename Node2>
550 [[nodiscard]] Node1
551 follow_path_no_checks(WordGraph<Node1> const& wg,
552 Node2 from,
553 word_type const& path) noexcept {
554 static_assert(sizeof(Node2) <= sizeof(Node1));
555 return follow_path_no_checks(
556 WordGraphView<Node1>(wg), from, path.cbegin(), path.cend());
557 }
558
592 // Not noexcept because detail::is_acyclic isn't
593 template <typename Node>
594 [[nodiscard]] bool is_acyclic(WordGraphView<Node> const& wg);
595
628 // Not noexcept because detail::is_acyclic isn't
629 template <typename Node>
630 [[nodiscard]] bool is_acyclic(WordGraph<Node> const& wg) {
631 return is_acyclic(WordGraphView<Node>(wg));
632 }
633
679 // Not noexcept because detail::is_acyclic isn't
680 template <typename Node1, typename Node2>
681 [[nodiscard]] bool is_acyclic(WordGraphView<Node1> const& wg,
682 Node2 source);
683
728 // Not noexcept because detail::is_acyclic isn't
729 template <typename Node1, typename Node2>
730 [[nodiscard]] bool is_acyclic(WordGraph<Node1> const& wg, Node2 source) {
731 return is_acyclic(WordGraphView<Node1>(wg), source);
732 }
733
764 // Not noexcept because detail::is_acyclic isn't
765 template <typename Node1, typename Node2>
766 [[nodiscard]] bool is_acyclic(WordGraphView<Node1> const& wg,
767 Node2 source,
768 Node2 target);
769
799 // Not noexcept because detail::is_acyclic isn't
800 template <typename Node1, typename Node2>
801 [[nodiscard]] bool is_acyclic(WordGraph<Node1> const& wg,
802 Node2 source,
803 Node2 target) {
804 return is_acyclic(WordGraphView<Node1>(wg), source, target);
805 }
806
846 // TODO(1) add a version of this function with one that returns a float
847 // representing the proportion of the nodes in the range that are
848 // compatible with the rules. Don't replace the current version because it
849 // can return early knowing that it isn't compatible.
850 template <typename Node,
851 typename Iterator1,
852 typename Iterator2,
853 typename Iterator3>
854 [[nodiscard]] bool is_compatible_no_checks(WordGraphView<Node> const& wg,
855 Iterator1 first_node,
856 Iterator2 last_node,
857 Iterator3 first_rule,
858 Iterator3 last_rule);
859
899 template <typename Node,
900 typename Iterator1,
901 typename Iterator2,
902 typename Iterator3>
903 [[nodiscard]] bool is_compatible_no_checks(WordGraph<Node> const& wg,
904 Iterator1 first_node,
905 Iterator2 last_node,
906 Iterator3 first_rule,
907 Iterator3 last_rule) {
908 return is_compatible_no_checks(WordGraphView<Node>(wg),
909 first_node,
910 last_node,
911 first_rule,
912 last_rule);
913 }
914
956 template <typename Node,
957 typename Iterator1,
958 typename Iterator2,
959 typename Iterator3,
960 typename = std::enable_if_t<
961 !std::is_same_v<std::decay_t<Iterator3>, word_type>>>
962 [[nodiscard]] bool is_compatible(WordGraphView<Node> const& wg,
963 Iterator1 first_node,
964 Iterator2 last_node,
965 Iterator3 first_rule,
966 Iterator3 last_rule);
967
1011 template <typename Node,
1012 typename Iterator1,
1013 typename Iterator2,
1014 typename Iterator3,
1015 typename = std::enable_if_t<
1016 !std::is_same_v<std::decay_t<Iterator3>, word_type>>>
1017 [[nodiscard]] bool is_compatible(WordGraph<Node> const& wg,
1018 Iterator1 first_node,
1019 Iterator2 last_node,
1020 Iterator3 first_rule,
1021 Iterator3 last_rule) {
1022 WordGraphView<Node> wgv(wg);
1023 return is_compatible(wgv, first_node, last_node, first_rule, last_rule);
1024 }
1025
1058 template <typename Node, typename Iterator1, typename Iterator2>
1059 bool is_compatible_no_checks(WordGraphView<Node> const& wg,
1060 Iterator1 first_node,
1061 Iterator2 last_node,
1062 word_type const& lhs,
1063 word_type const& rhs);
1064
1096 template <typename Node, typename Iterator1, typename Iterator2>
1097 bool is_compatible_no_checks(WordGraph<Node> const& wg,
1098 Iterator1 first_node,
1099 Iterator2 last_node,
1100 word_type const& lhs,
1101 word_type const& rhs) {
1102 return is_compatible_no_checks(
1103 WordGraphView<Node>(wg), first_node, last_node, lhs, rhs);
1104 }
1105
1144 template <typename Node, typename Iterator1, typename Iterator2>
1145 bool is_compatible(WordGraphView<Node> const& wg,
1146 Iterator1 first_node,
1147 Iterator2 last_node,
1148 word_type const& lhs,
1149 word_type const& rhs);
1150
1188 template <typename Node, typename Iterator1, typename Iterator2>
1189 bool is_compatible(WordGraph<Node> const& wg,
1190 Iterator1 first_node,
1191 Iterator2 last_node,
1192 word_type const& lhs,
1193 word_type const& rhs) {
1194 return is_compatible(
1195 WordGraphView<Node>(wg), first_node, last_node, lhs, rhs);
1196 }
1197
1198 /*
1242 TODO(0): Remove or delete
1243 template <typename WordGraphType,
1244 typename Iterator1,
1245 typename Iterator2,
1246 typename Iterator3,
1247 typename = std::enable_if_t<
1248 std::is_same_v<WordGraphType,
1249 WordGraph<typename
1250 WordGraphType::node_type>>
1251 || std::is_same_v<
1252 WordGraphType,
1253 WordGraphView<typename WordGraphType::node_type>>>>
1254 bool is_compatible(WordGraphType const& wg,
1255 Iterator1 first_node,
1256 Iterator2 last_node,
1257 Iterator3 first_rule,
1258 Iterator3 last_rule) {
1259 if constexpr (std::is_same_v<
1260 WordGraphType,
1261 WordGraph<typename WordGraphType::node_type>>) {
1262 return is_compatible(
1263 static_cast<WordGraph<typename WordGraphType::node_type>
1264 const&>(
1265 wg),
1266 first_node,
1267 last_node,
1268 first_rule,
1269 last_rule);
1270 } else {
1271 return is_compatible(
1272 static_cast<
1273 WordGraphView<typename WordGraphType::node_type>
1274 const&>(wg),
1275 first_node,
1276 last_node,
1277 first_rule,
1278 last_rule);
1279 }
1280 }
1281 */
1282
1315 template <typename Node, typename Iterator1, typename Iterator2>
1316 [[nodiscard]] bool is_complete_no_checks(WordGraphView<Node> const& wg,
1317 Iterator1 first_node,
1318 Iterator2 last_node);
1319
1351 template <typename Node, typename Iterator1, typename Iterator2>
1352 [[nodiscard]] bool is_complete_no_checks(WordGraph<Node> const& wg,
1353 Iterator1 first_node,
1354 Iterator2 last_node) {
1355 return is_complete_no_checks(
1356 WordGraphView<Node>(wg), first_node, last_node);
1357 }
1358
1388 template <typename Node, typename Iterator1, typename Iterator2>
1389 [[nodiscard]] bool is_complete(WordGraphView<Node> const& wg,
1390 Iterator1 first_node,
1391 Iterator2 last_node);
1392
1422 template <typename Node, typename Iterator1, typename Iterator2>
1423 [[nodiscard]] bool is_complete(WordGraph<Node> const& wg,
1424 Iterator1 first_node,
1425 Iterator2 last_node) {
1426 return is_complete(WordGraphView<Node>(wg), first_node, last_node);
1427 }
1428
1447 template <typename Node>
1448 [[nodiscard]] bool
1449 is_complete_no_checks(WordGraphView<Node> const& wg) noexcept {
1450 return wg.number_of_edges_no_checks()
1451 == wg.number_of_nodes_no_checks() * wg.out_degree_no_checks();
1452 }
1453
1454 template <typename Node>
1455 [[nodiscard]] bool is_complete(WordGraphView<Node> const& wg) {
1456 wg.throw_if_invalid_view();
1457 return is_complete_no_checks(wg);
1458 }
1459
1478 template <typename Node>
1479 [[nodiscard]] bool is_complete(WordGraph<Node> const& wg) noexcept {
1480 return is_complete(WordGraphView<Node>(wg));
1481 }
1482
1507 template <typename Node>
1508 [[nodiscard]] bool is_connected(WordGraphView<Node> const& wg);
1509
1533 template <typename Node>
1534 [[nodiscard]] bool is_connected(WordGraph<Node> const& wg) {
1535 return is_connected(WordGraphView<Node>(wg));
1536 }
1537
1588 template <typename Node1, typename Node2>
1589 [[nodiscard]] bool is_reachable_no_checks(WordGraphView<Node1> const& wg,
1590 Node2 source,
1591 Node2 target);
1592
1642 template <typename Node1, typename Node2>
1643 [[nodiscard]] bool is_reachable_no_checks(WordGraph<Node1> const& wg,
1644 Node2 source,
1645 Node2 target) {
1646 return is_reachable_no_checks(WordGraphView<Node1>(wg), source, target);
1647 }
1648
1681 template <typename Node1, typename Node2>
1682 [[nodiscard]] bool is_reachable(WordGraphView<Node1> const& wg,
1683 Node2 source,
1684 Node2 target);
1685
1718 template <typename Node1, typename Node2>
1719 [[nodiscard]] bool is_reachable(WordGraph<Node1> const& wg,
1720 Node2 source,
1721 Node2 target) {
1722 return is_reachable(WordGraphView<Node1>(wg), source, target);
1723 }
1724
1757 // TODO(1) should have a version that returns the node that everything is
1758 // reachable from
1759 template <typename Node>
1760 [[nodiscard]] bool is_strictly_cyclic(WordGraphView<Node> const& wg);
1761
1794 // TODO(1) should have a version that returns the node that everything is
1795 // reachable from
1796 template <typename Node>
1797 [[nodiscard]] bool is_strictly_cyclic(WordGraph<Node> const& wg) {
1798 return is_strictly_cyclic(WordGraphView<Node>(wg));
1799 }
1800
1827 template <typename Node1, typename Node2, typename Iterator>
1828 [[nodiscard]] std::pair<Node1, Iterator>
1829 last_node_on_path_no_checks(WordGraphView<Node1> const& wg,
1830 Node2 source,
1831 Iterator first,
1832 Iterator last) noexcept;
1833
1860 template <typename Node1, typename Node2, typename Iterator>
1861 [[nodiscard]] std::pair<Node1, Iterator>
1862 last_node_on_path_no_checks(WordGraph<Node1> const& wg,
1863 Node2 source,
1864 Iterator first,
1865 Iterator last) noexcept {
1866 return last_node_on_path_no_checks(
1867 WordGraphView<Node1>(wg), source, first, last);
1868 }
1869
1895 template <typename Node1, typename Node2, typename Iterator>
1896 [[nodiscard]] std::pair<Node1, Iterator>
1897 last_node_on_path(WordGraphView<Node1> const& wg,
1898 Node2 source,
1899 Iterator first,
1900 Iterator last);
1901
1927 template <typename Node1, typename Node2, typename Iterator>
1928 [[nodiscard]] std::pair<Node1, Iterator>
1929 last_node_on_path(WordGraph<Node1> const& wg,
1930 Node2 source,
1931 Iterator first,
1932 Iterator last) {
1933 return last_node_on_path(WordGraphView<Node1>(wg), source, first, last);
1934 }
1935
1957 template <typename Node1, typename Node2>
1958 std::pair<Node1, word_type::const_iterator>
1959 last_node_on_path_no_checks(WordGraphView<Node1> const& wg,
1960 Node2 source,
1961 word_type const& w);
1962
1984 template <typename Node1, typename Node2>
1985 std::pair<Node1, word_type::const_iterator>
1986 last_node_on_path_no_checks(WordGraph<Node1> const& wg,
1987 Node2 source,
1988 word_type const& w) {
1989 return last_node_on_path_no_checks(WordGraphView<Node1>(wg), source, w);
1990 }
1991
2013 template <typename Node1, typename Node2>
2014 std::pair<Node1, word_type::const_iterator>
2015 last_node_on_path(WordGraphView<Node1> const& wg,
2016 Node2 source,
2017 word_type const& w);
2018
2040 template <typename Node1, typename Node2>
2041 std::pair<Node1, word_type::const_iterator>
2042 last_node_on_path(WordGraph<Node1> const& wg,
2043 Node2 source,
2044 word_type const& w) {
2045 return last_node_on_path(WordGraphView<Node1>(wg), source, w);
2046 }
2047
2068 // TODO(1) tests
2069 // TODO(1) version where std::unordered_set is passed by reference, or
2070 // make this a class that stores its stack and unordered_set, not clear
2071 // why we'd single out the unordered_set to be passed by reference.
2072 // TODO(2) version which is an iterator i.e. returns an iterator or range
2073 // object that allows use to step through the nodes reachable from a given
2074 // node
2075 template <typename Node1, typename Node2>
2076 [[nodiscard]] std::unordered_set<Node1>
2077 nodes_reachable_from(WordGraphView<Node1> const& wg, Node2 source);
2078
2099 // TODO(1) tests
2100 // TODO(1) version where std::unordered_set is passed by reference, or
2101 // make this a class that stores its stack and unordered_set, not clear
2102 // why we'd single out the unordered_set to be passed by reference.
2103 // TODO(2) version which is an iterator i.e. returns an iterator or range
2104 // object that allows use to step through the nodes reachable from a given
2105 // node
2106 template <typename Node1, typename Node2>
2107 [[nodiscard]] std::unordered_set<Node1>
2108 nodes_reachable_from(WordGraph<Node1> const& wg, Node2 source) {
2109 return nodes_reachable_from(WordGraphView<Node1>(wg), source);
2110 }
2111
2132 template <typename Node1, typename Node2>
2133 [[nodiscard]] std::unordered_set<Node1>
2134 ancestors_of(WordGraphView<Node1> const& wg, Node2 target);
2135
2156 template <typename Node1, typename Node2>
2157 [[nodiscard]] std::unordered_set<Node1>
2158 ancestors_of(WordGraph<Node1> const& wg, Node2 target) {
2159 return ancestors_of(WordGraphView<Node1>(wg), target);
2160 }
2161
2183 template <typename Node1, typename Node2>
2184 [[nodiscard]] std::unordered_set<Node1>
2185 nodes_reachable_from_no_checks(WordGraph<Node1> const& wg, Node2 source);
2186
2208 template <typename Node1, typename Node2>
2209 [[nodiscard]] std::unordered_set<Node1>
2210 ancestors_of_no_checks(WordGraphView<Node1> const& wg, Node2 target);
2211
2233 template <typename Node1, typename Node2>
2234 [[nodiscard]] std::unordered_set<Node1>
2235 ancestors_of_no_checks(WordGraph<Node1> const& wg, Node2 target) {
2236 return ancestors_of_no_checks(WordGraphView<Node1>(wg), target);
2237 }
2238
2259 template <typename Node1, typename Node2>
2260 [[nodiscard]] size_t
2261 number_of_nodes_reachable_from(WordGraphView<Node1> const& wg,
2262 Node2 source) {
2263 return nodes_reachable_from(wg, source).size();
2264 }
2265
2286 template <typename Node1, typename Node2>
2287 [[nodiscard]] size_t
2288 number_of_nodes_reachable_from(WordGraph<Node1> const& wg, Node2 source) {
2289 return number_of_nodes_reachable_from(WordGraphView<Node1>(wg), source);
2290 }
2291
2310 template <typename Node1, typename Node2>
2311 [[nodiscard]] size_t
2312 number_of_nodes_reachable_from_no_checks(WordGraphView<Node1> const& wg,
2313 Node2 source) {
2314 return nodes_reachable_from_no_checks(wg, source).size();
2315 }
2316
2335 template <typename Node1, typename Node2>
2336 [[nodiscard]] size_t
2337 number_of_nodes_reachable_from_no_checks(WordGraph<Node1> const& wg,
2338 Node2 source) {
2339 return number_of_nodes_reachable_from_no_checks(
2340 WordGraphView<Node1>(wg), source);
2341 }
2342
2364 template <typename Node>
2365 WordGraph<Node> random_acyclic(size_t number_of_nodes,
2366 size_t out_degree,
2367 std::mt19937 mt
2368 = std::mt19937(std::random_device()()));
2369
2389 template <typename Node1, typename Node2>
2390 void spanning_tree_no_checks(WordGraphView<Node1> const& wg,
2391 Node2 root,
2392 Forest& f);
2393
2413 template <typename Node1, typename Node2>
2414 void spanning_tree_no_checks(WordGraph<Node1> const& wg,
2415 Node2 root,
2416 Forest& f) {
2417 return spanning_tree_no_checks(WordGraphView<Node1>(wg), root, f);
2418 }
2419
2438 template <typename Node1, typename Node2>
2439 void spanning_tree(WordGraphView<Node1> const& wg, Node2 root, Forest& f);
2440
2459 template <typename Node1, typename Node2>
2460 void spanning_tree(WordGraph<Node1> const& wg, Node2 root, Forest& f) {
2461 spanning_tree(WordGraphView<Node1>(wg), root, f);
2462 }
2463
2484 template <typename Node1, typename Node2>
2485 [[nodiscard]] Forest
2486 spanning_tree_no_checks(WordGraphView<Node1> const& wg, Node2 root);
2487
2508 template <typename Node1, typename Node2>
2509 [[nodiscard]] Forest spanning_tree_no_checks(WordGraph<Node1> const& wg,
2510 Node2 root) {
2511 return spanning_tree_no_checks(WordGraphView<Node1>(wg), root);
2512 }
2513
2533 template <typename Node1, typename Node2>
2534 [[nodiscard]] Forest spanning_tree(WordGraphView<Node1> const& wg,
2535 Node2 root);
2536
2556 template <typename Node1, typename Node2>
2557 [[nodiscard]] Forest spanning_tree(WordGraph<Node1> const& wg,
2558 Node2 root) {
2559 return spanning_tree(WordGraphView<Node1>(wg), root);
2560 }
2561
2585 // Not nodiscard because sometimes we just don't want the output
2586 template <typename Graph>
2587 bool standardize(Graph& wg, Forest& f, Order val);
2588
2613 // Not nodiscard because sometimes we just don't want the output
2614 template <typename Graph>
2615 std::pair<bool, Forest> standardize(Graph& wg,
2616 Order val = Order::shortlex);
2617
2634 template <typename Node>
2635 bool is_standardized(WordGraphView<Node> const& wg,
2636 Order val = Order::shortlex);
2637
2654 template <typename Node>
2655 bool is_standardized(WordGraph<Node> const& wg,
2656 Order val = Order::shortlex) {
2657 return is_standardized(WordGraphView<Node>(wg), val);
2658 }
2659
2683 template <typename Node>
2684 [[nodiscard]] std::vector<Node>
2685 topological_sort(WordGraphView<Node> const& wg);
2686
2710 template <typename Node>
2711 [[nodiscard]] std::vector<Node>
2712 topological_sort(WordGraph<Node> const& wg) {
2713 return topological_sort(WordGraphView<Node>(wg));
2714 }
2715
2742 template <typename Node1, typename Node2>
2743 [[nodiscard]] std::vector<Node1>
2744 topological_sort(WordGraphView<Node1> const& wg, Node2 source);
2745
2772 template <typename Node1, typename Node2>
2773 [[nodiscard]] std::vector<Node1>
2774 topological_sort(WordGraph<Node1> const& wg, Node2 source) {
2775 return topological_sort(WordGraphView<Node1>(wg), source);
2776 }
2777
2778 } // namespace word_graph
2779
2781 // WordGraph - non-member functions
2783 // TODO(1) Add equivalents for WordGraphView
2784
2804 template <typename Node>
2806
2817
2846 // Passing the 2nd parameter "targets" by value disambiguates it from the
2847 // other make<WordGraph>.
2848 template <typename Return>
2849 [[nodiscard]] std::enable_if_t<is_specialization_of_v<Return, WordGraph>,
2850 Return>
2852 size_t num_nodes,
2854
2857 // clang-format off
2858 // NOLINTNEXTLINE(whitespace/line_length)
2860 // clang-format on
2861 template <typename Return>
2862 [[nodiscard]] std::enable_if_t<is_specialization_of_v<Return, WordGraph>,
2863 Return>
2864 make(size_t num_nodes,
2866
2867 namespace detail {
2868 template <typename Subclass>
2869 class JoinerMeeterCommon {
2870 private:
2871 template <typename Node1, typename Node2>
2872 void throw_if_bad_args(WordGraph<Node1> const& x,
2873 Node2 xroot,
2874 WordGraph<Node1> const& y,
2875 Node2 yroot);
2876
2877 public:
2878 template <typename Node>
2879 void call_no_checks(WordGraph<Node>& xy,
2880 WordGraph<Node> const& x,
2881 Node xroot,
2882 WordGraph<Node> const& y,
2883 Node yroot);
2884
2885 template <typename Node>
2886 void call_no_checks(WordGraph<Node>& xy,
2887 WordGraph<Node> const& x,
2888 WordGraph<Node> const& y) {
2889 return call_no_checks(
2890 xy, x, static_cast<Node>(0), y, static_cast<Node>(0));
2891 }
2892
2893 template <typename Node, typename... Args>
2894 [[nodiscard]] auto call_no_checks(WordGraph<Node> const& x,
2895 Args&&... args)
2896 -> std::enable_if_t<sizeof...(Args) % 2 == 1, WordGraph<Node>> {
2897 // The versions of this function changing the 1st argument in-place
2898 // always have an odd number of arguments, so we check that it's even
2899 // here (the argument x and an odd number of further arguments).
2900 WordGraph<Node> xy;
2901 static_cast<Subclass&>(*this).call_no_checks(
2902 xy, x, std::forward<Args>(args)...);
2903 return xy;
2904 }
2905
2906 // There's no operator() with the number of nodes reachable from the
2907 // roots as arguments (7 args in total) because we'd have to check that
2908 // they were valid, and the only way to do this is to recompute them.
2909
2910 template <typename Node>
2911 void operator()(WordGraph<Node>& xy,
2912 WordGraph<Node> const& x,
2913 Node xroot,
2914 WordGraph<Node> const& y,
2915 Node yroot) {
2916 throw_if_bad_args(x, xroot, y, yroot);
2917 call_no_checks(xy, x, xroot, y, yroot);
2918 }
2919
2920 template <typename Node>
2921 void operator()(WordGraph<Node>& xy,
2922 WordGraph<Node> const& x,
2923 WordGraph<Node> const& y) {
2924 return operator()(
2925 xy, x, static_cast<Node>(0), y, static_cast<Node>(0));
2926 }
2927
2928 template <typename Node, typename... Args>
2929 [[nodiscard]] auto operator()(WordGraph<Node> const& x, Args&&... args)
2930 -> std::enable_if_t<sizeof...(Args) % 2 == 1, WordGraph<Node>> {
2931 // The versions of this function changing the 1st argument in-place
2932 // always have an odd number of arguments, so we check that it's even
2933 // here (the argument x and an odd number of further arguments).
2934 WordGraph<Node> xy;
2935 operator()(xy, x, std::forward<Args>(args)...);
2936 return xy;
2937 }
2938
2939 template <typename Node1, typename Node2>
2940 bool is_subrelation_no_checks(WordGraph<Node1> const& x,
2941 Node2 xroot,
2942 WordGraph<Node1> const& y,
2943 Node2 yroot);
2944
2945 template <typename Node>
2946 bool is_subrelation_no_checks(WordGraph<Node> const& x,
2947 WordGraph<Node> const& y) {
2948 return is_subrelation_no_checks(
2949 x, static_cast<Node>(0), y, static_cast<Node>(0));
2950 }
2951
2952 // There's no subrelation with the number of nodes reachable from the
2953 // roots as arguments (6 args in total) because we'd have to check that
2954 // they were valid, and the only way to do this is to recompute them.
2955
2956 template <typename Node1, typename Node2>
2957 bool is_subrelation(WordGraph<Node1> const& x,
2958 Node2 xroot,
2959 WordGraph<Node1> const& y,
2960 Node2 yroot) {
2961 throw_if_bad_args(x, xroot, y, yroot);
2962 return is_subrelation_no_checks(x, xroot, y, yroot);
2963 }
2964
2965 template <typename Node>
2966 bool is_subrelation(WordGraph<Node> const& x,
2967 WordGraph<Node> const& y) {
2968 return is_subrelation(
2969 x, static_cast<Node>(0), y, static_cast<Node>(0));
2970 }
2971 }; // JoinerMeeterCommon
2972 } // namespace detail
2973
2985 // This class is intentionally not a template so that we don't have to
2986 // specify the types of the nodes when constructing one of these objects.
2987 // Instead every member function has a template parameter Node, which is
2988 // deduced from the argument.
2989 class Joiner : public detail::JoinerMeeterCommon<Joiner> {
2990 private:
2991 // TODO(v4) Remove the libsemigroups prefix
2992 libsemigroups::detail::Duf<> _uf;
2994 std::vector<uint64_t> _lookup;
2995
2996 template <typename Node>
2997 [[nodiscard]] Node find(WordGraph<Node> const& x,
2998 size_t xnum_nodes_reachable_from_root,
2999 WordGraph<Node> const& y,
3000 uint64_t n,
3001 typename WordGraph<Node>::label_type a) const;
3002
3003 template <typename Node>
3004 void run(WordGraph<Node> const& x,
3005 size_t xnum_nodes_reachable_from_root,
3006 Node xroot,
3007 WordGraph<Node> const& y,
3008 size_t ynum_nodes_reachable_from_root,
3009 Node yroot);
3010
3011 public:
3016
3021
3026
3031
3036
3037 ~Joiner();
3038
3067 template <typename Node>
3069 WordGraph<Node> const& x,
3070 size_t xnum_nodes_reachable_from_root,
3071 Node xroot,
3072 WordGraph<Node> const& y,
3073 size_t ynum_nodes_reachable_from_root,
3074 Node yroot);
3075
3109 // Is x a subrelation of y?
3110 template <typename Node1, typename Node2>
3111 [[nodiscard]] bool
3113 size_t xnum_nodes_reachable_from_root,
3114 Node2 xroot,
3115 WordGraph<Node1> const& y,
3116 size_t ynum_nodes_reachable_from_root,
3117 Node2 yroot);
3118#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3142 template <typename Node>
3144 WordGraph<Node> const& x,
3145 Node xroot,
3146 WordGraph<Node> const& y,
3147 Node yroot);
3148
3168 template <typename Node>
3170 WordGraph<Node> const& x,
3171 WordGraph<Node> const& y);
3172
3192 template <typename Node, typename... Args>
3193 [[nodiscard]] auto call_no_checks(WordGraph<Node> const& x,
3194 Args&&... args);
3195
3221 template <typename Node>
3223 WordGraph<Node> const& x,
3224 Node xroot,
3225 WordGraph<Node> const& y,
3226 Node yroot);
3227
3249 template <typename Node>
3251 WordGraph<Node> const& x,
3252 WordGraph<Node> const& y);
3253
3271 template <typename Node, typename... Args>
3272 [[nodiscard]] auto operator()(WordGraph<Node> const& x, Args&&... args);
3273
3303 // Is x a subrelation of y?
3304 template <typename Node1, typename Node2>
3306 Node2 xroot,
3307 WordGraph<Node1> const& y,
3308 Node2 yroot);
3309
3332 template <typename Node>
3334 WordGraph<Node> const& y);
3335
3336 // There's no subrelation with the number of nodes reachable from the
3337 // roots as arguments (6 args in total) because we'd have to check that
3338 // they were valid, and the only way to do this is to recompute them.
3339
3368 template <typename Node1, typename Node2>
3370 Node2 xroot,
3371 WordGraph<Node1> const& y,
3372 Node2 yroot);
3373
3398 template <typename Node>
3400
3401#else
3402 using detail::JoinerMeeterCommon<Joiner>::call_no_checks;
3403 using detail::JoinerMeeterCommon<Joiner>::operator();
3404 using detail::JoinerMeeterCommon<Joiner>::is_subrelation_no_checks;
3405 using detail::JoinerMeeterCommon<Joiner>::is_subrelation;
3406#endif
3407 }; // Joiner
3408
3421 // Class for forming the meet of two word graphs
3422 class Meeter : public detail::JoinerMeeterCommon<Meeter> {
3423 private:
3424 using node_type = std::pair<uint64_t, uint64_t>;
3425
3428 std::vector<node_type> _todo_new;
3429
3430 public:
3435
3440
3445
3450
3455
3456 ~Meeter();
3457
3460 template <typename Node>
3462 WordGraph<Node> const& x,
3463 size_t xnum_nodes_reachable_from_root,
3464 Node xroot,
3465 WordGraph<Node> const& y,
3466 size_t ynum_nodes_reachable_from_root,
3467 Node yroot);
3468
3471 // is x a subrelation of y
3472 template <typename Node1, typename Node2>
3473 [[nodiscard]] bool
3475 size_t xnum_nodes_reachable_from_root,
3476 Node2 xroot,
3477 WordGraph<Node1> const& y,
3478 size_t ynum_nodes_reachable_from_root,
3479 Node2 yroot);
3480
3481#ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
3484 template <typename Node>
3486 WordGraph<Node> const& x,
3487 Node xroot,
3488 WordGraph<Node> const& y,
3489 Node yroot);
3490
3493 template <typename Node>
3495 WordGraph<Node> const& x,
3496 WordGraph<Node> const& y);
3497
3517 template <typename Node, typename... Args>
3518 [[nodiscard]] auto call_no_checks(WordGraph<Node> const& x,
3519 Args&&... args);
3520
3523 template <typename Node>
3525 WordGraph<Node> const& x,
3526 Node xroot,
3527 WordGraph<Node> const& y,
3528 Node yroot);
3529
3532 template <typename Node>
3534 WordGraph<Node> const& x,
3535 WordGraph<Node> const& y);
3536
3554 template <typename Node, typename... Args>
3555 [[nodiscard]] auto operator()(WordGraph<Node> const& x, Args&&... args);
3556
3559 template <typename Node1, typename Node2>
3561 Node2 xroot,
3562 WordGraph<Node1> const& y,
3563 Node2 yroot);
3564
3567 template <typename Node>
3569 WordGraph<Node> const& y);
3570
3573 template <typename Node1, typename Node2>
3575 Node2 xroot,
3576 WordGraph<Node1> const& y,
3577 Node2 yroot);
3578
3581 template <typename Node>
3583#else
3584 using detail::JoinerMeeterCommon<Meeter>::call_no_checks;
3585 using detail::JoinerMeeterCommon<Meeter>::operator();
3586 using detail::JoinerMeeterCommon<Meeter>::is_subrelation_no_checks;
3587 using detail::JoinerMeeterCommon<Meeter>::is_subrelation;
3588#endif
3589 }; // class Meeter
3590
3605 template <typename Node>
3607
3620 [[nodiscard]] static inline std::string
3622 (void) meet;
3623 return "<Meeter of word graphs>";
3624 }
3625
3638 [[nodiscard]] static inline std::string
3640 (void) join;
3641 return "<Joiner of word graphs>";
3642 }
3643
3664 template <typename Node>
3666 std::string const& prefix = "",
3667 std::string const& braces = "{}",
3668 std::string const& suffix = "");
3669
3670 } // namespace v4
3671} // namespace libsemigroups
3672
3673#include "libsemigroups/word-graph-helpers.tpp"
3674#endif // LIBSEMIGROUPS_WORD_GRAPH_HELPERS_HPP_
Class for representing word graphs.
Definition word-graph.hpp:83
Node label_type
The type of edge labels in a word graph.
Definition word-graph.hpp:102
Class for taking joins of word graphs.
Definition word-graph-helpers.hpp:2989
bool is_subrelation_no_checks(WordGraph< Node > const &x, WordGraph< Node > const &y)
Check if the language accepted by one word graph is contained in that defined by another word graph.
auto call_no_checks(WordGraph< Node > const &x, Args &&... args)
Returns a word graph containing the join/meet of two given word graphs.
auto operator()(WordGraph< Node > const &x, Args &&... args)
Returns a word graph containing the join/meet of two given word graphs.
Joiner(Joiner const &)
Default copy constructor.
void call_no_checks(WordGraph< Node > &xy, WordGraph< Node > const &x, Node xroot, WordGraph< Node > const &y, Node yroot)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
bool is_subrelation(WordGraph< Node1 > const &x, Node2 xroot, WordGraph< Node1 > const &y, Node2 yroot)
Check if the language accepted by one word graph is contained in that defined by another word graph.
Joiner(Joiner &&)
Default move constructor.
bool is_subrelation_no_checks(WordGraph< Node1 > const &x, Node2 xroot, WordGraph< Node1 > const &y, Node2 yroot)
Check if the language accepted by one word graph is contained in that defined by another word graph.
Joiner()
Default constructor.
Joiner & operator=(Joiner const &)
Default copy assignment operator.
void call_no_checks(WordGraph< Node > &xy, WordGraph< Node > const &x, WordGraph< Node > const &y)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
Joiner & operator=(Joiner &&)
Default move assignment operator.
bool is_subrelation(WordGraph< Node > const &x, WordGraph< Node > const &y)
Check if the language accepted by one word graph is contained in that defined by another word graph.
void operator()(WordGraph< Node > &xy, WordGraph< Node > const &x, WordGraph< Node > const &y)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
void call_no_checks(WordGraph< Node > &xy, WordGraph< Node > const &x, size_t xnum_nodes_reachable_from_root, Node xroot, WordGraph< Node > const &y, size_t ynum_nodes_reachable_from_root, Node yroot)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
bool is_subrelation_no_checks(WordGraph< Node1 > const &x, size_t xnum_nodes_reachable_from_root, Node2 xroot, WordGraph< Node1 > const &y, size_t ynum_nodes_reachable_from_root, Node2 yroot)
Check if the language accepted by one word graph is contained in that accepted by another word graph.
void operator()(WordGraph< Node > &xy, WordGraph< Node > const &x, Node xroot, WordGraph< Node > const &y, Node yroot)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
Class for taking meets of word graphs.
Definition word-graph-helpers.hpp:3422
bool is_subrelation_no_checks(WordGraph< Node > const &x, WordGraph< Node > const &y)
Check if the language accepted by one word graph is contained in that defined by another word graph.
auto call_no_checks(WordGraph< Node > const &x, Args &&... args)
Returns a word graph containing the join/meet of two given word graphs.
auto operator()(WordGraph< Node > const &x, Args &&... args)
Returns a word graph containing the join/meet of two given word graphs.
void call_no_checks(WordGraph< Node > &xy, WordGraph< Node > const &x, Node xroot, WordGraph< Node > const &y, Node yroot)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
bool is_subrelation(WordGraph< Node1 > const &x, Node2 xroot, WordGraph< Node1 > const &y, Node2 yroot)
Check if the language accepted by one word graph is contained in that defined by another word graph.
bool is_subrelation_no_checks(WordGraph< Node1 > const &x, Node2 xroot, WordGraph< Node1 > const &y, Node2 yroot)
Check if the language accepted by one word graph is contained in that defined by another word graph.
Meeter & operator=(Meeter const &)
Default copy assignment operator.
void call_no_checks(WordGraph< Node > &xy, WordGraph< Node > const &x, WordGraph< Node > const &y)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
Meeter()
Default constructor.
Meeter & operator=(Meeter &&)
Default move assignment operator.
bool is_subrelation(WordGraph< Node > const &x, WordGraph< Node > const &y)
Check if the language accepted by one word graph is contained in that defined by another word graph.
void operator()(WordGraph< Node > &xy, WordGraph< Node > const &x, WordGraph< Node > const &y)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
Meeter(Meeter &&)
Default move constructor.
void call_no_checks(WordGraph< Node > &xy, WordGraph< Node > const &x, size_t xnum_nodes_reachable_from_root, Node xroot, WordGraph< Node > const &y, size_t ynum_nodes_reachable_from_root, Node yroot)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
Meeter(Meeter const &)
Default copy constructor.
bool is_subrelation_no_checks(WordGraph< Node1 > const &x, size_t xnum_nodes_reachable_from_root, Node2 xroot, WordGraph< Node1 > const &y, size_t ynum_nodes_reachable_from_root, Node2 yroot)
Check if the language accepted by one word graph is contained in that accepted by another word graph.
void operator()(WordGraph< Node > &xy, WordGraph< Node > const &x, Node xroot, WordGraph< Node > const &y, Node yroot)
Replace the contents of a word graph with the join/meet of two given word graphs with respect to give...
T forward(T... args)
std::enable_if_t< is_specialization_of_v< Return, WordGraph >, Return > make(size_t num_nodes, std::initializer_list< std::vector< typename Return::node_type > > targets)
Constructs a word graph from a number of nodes and targets.
std::vector< letter_type > word_type
Type for a word over the generators of a semigroup.
Definition types.hpp:99
Order
The possible orderings of words and strings.
Definition order.hpp:54
@ shortlex
Definition order.hpp:60
std::ostream & operator<<(std::ostream &os, WordGraph< Node > const &wg)
std::string to_human_readable_repr(WordGraph< Node > const &wg)
Return a human readable representation of a WordGraph object.
std::string to_input_string(WordGraph< Node > const &wg, std::string const &prefix="", std::string const &braces="{}", std::string const &suffix="")
Return a string that can be used to recreate a word graph.
Namespace for everything in the libsemigroups library.
Definition action.hpp:44