LAL: Linear Arrangement Library 21.07.01
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
tree_isomorphism.hpp
1#pragma once
2
3// C++ includes
4#include <algorithm>
5#include <cinttypes>
6#if defined DEBUG
7#include <cassert>
8#endif
9
10// lal includes
11#include <lal/graphs/rooted_tree.hpp>
12#include <lal/internal/data_array.hpp>
13
14namespace lal {
15namespace internal {
16
17/*
18 * Returns whether the input trees are, might be, or are not isomorphic.
19 *
20 * Returns 0 if the trees ARE isomorphic
21 * Returns 1 if the trees ARE NOT isomorphic:
22 * - number of vertices do not coincide
23 * - number of leaves do not coincide
24 * - second moment of degree do not coincide
25 * Returns 2 if the trees MIGHT BE isomorphic
26 */
27template<
28 class T,
29 std::enable_if_t<
30 std::is_base_of_v<graphs::free_tree, T> ||
31 std::is_base_of_v<graphs::rooted_tree, T>,
32 bool> = true
33>
34inline
35char fast_non_iso(const T& t1, const T& t2) noexcept {
36 // check number of nodes
37 if (t1.get_num_nodes() != t2.get_num_nodes()) { return 1; }
38
39 if constexpr (std::is_base_of_v<lal::graphs::rooted_tree, T>) {
40 // rooted trees must have correct orientation of edges
41 if (not t1.is_orientation_valid() or not t2.is_orientation_valid()) {
42 return false;
43 }
44 }
45
46 const uint32_t n = t1.get_num_nodes();
47
48 // trees ARE isomorphic
49 if (n <= 2) { return 0; }
50
51 uint32_t nL_t1 = 0; // number of leaves of t1
52 uint32_t nL_t2 = 0; // number of leaves of t2
53 uint64_t k2_t1 = 0; // sum of squared degrees of t1
54 uint64_t k2_t2 = 0; // sum of squared degrees of t2
55 uint64_t maxdeg_t1 = 0; // max degree of t1
56 uint64_t maxdeg_t2 = 0; // max degree of t2
57 for (node u = 0; u < n; ++u) {
58 const uint64_t ku1 = static_cast<uint64_t>(t1.get_degree(u));
59 const uint64_t ku2 = static_cast<uint64_t>(t2.get_degree(u));
60
61 nL_t1 += t1.get_degree(u) == 1;
62 nL_t2 += t2.get_degree(u) == 1;
63 k2_t1 += ku1*ku1;
64 k2_t2 += ku2*ku2;
65 maxdeg_t1 = (maxdeg_t1 < ku1 ? ku1 : maxdeg_t1);
66 maxdeg_t2 = (maxdeg_t2 < ku2 ? ku2 : maxdeg_t2);
67 }
68
69 // check number of leaves
70 if (nL_t1 != nL_t2) { return 1; }
71 // check maximum degree
72 if (maxdeg_t1 != maxdeg_t2) { return 1; }
73 // check sum of squared degrees
74 if (k2_t1 != k2_t2) { return 1; }
75
76 // trees MIGHT BE isomorphic
77 return 2;
78}
79
80/*
81 * @brief Assigns a name to node 'u', root of the current subtree.
82 *
83 * This function stores the names of every node in the subtree rooted at 'u'.
84 * This is useful if we want to make lots of comparisons between subtrees
85 *
86 * For further details on the algorithm, see \cite Aho1974a for further details.
87 * @param t Input rooted tree
88 * @param u Root of the subtree whose name we want to calculate
89 * @param names An array of strings where the names are stored (as in a dynamic
90 * programming algorithm). The size of this array must be at least the number of
91 * vertices in the subtree of 't' rooted at 'u'. Actually, less memory suffices,
92 * but I don't know how much less: better be safe than sorry.
93 * @param idx A pointer to the position within @e names that will contain the
94 * name of the first child of 'u'. The position @names[idx+1] will contain the
95 * name of the second child of 'u'.
96 * @returns The code for the subtree rooted at 'u'.
97 */
98inline
99void assign_name_and_keep(
100 const graphs::rooted_tree& t, node u,
101 std::string * const aux_memory_for_names, size_t idx,
102 std::string * const keep_name_of
103)
104noexcept
105{
106 if (t.get_out_degree(u) == 0) {
107 keep_name_of[u] = "10";
108 return;
109 }
110
111 // make childrens' names
112 const size_t begin_idx = idx;
113 for (node v : t.get_out_neighbours(u)) {
114 // make the name for v
115 assign_name_and_keep(t,v, aux_memory_for_names, idx+1, keep_name_of);
116
117 aux_memory_for_names[idx] = keep_name_of[v];
118 ++idx;
119 }
120 std::sort(&aux_memory_for_names[begin_idx], &aux_memory_for_names[idx]);
121
122 // join the names in a single string to make the name of vertex 'v'
123 std::string name = "1";
124 for (size_t j = begin_idx; j < idx; ++j) {
125 name += aux_memory_for_names[j];
126 }
127 name += "0";
128 keep_name_of[u] = name;
129}
130
131/*
132 * @brief Assigns a name to node 'u', root of the current subtree.
133 *
134 * For further details on the algorithm, see \cite Aho1974a for further details.
135 * @param t Input rooted tree
136 * @param u Root of the subtree whose name we want to calculate
137 * @param names An array of strings where the names are stored (as in a dynamic
138 * programming algorithm). The size of this array must be at least the number of
139 * vertices in the subtree of 't' rooted at 'u'. Actually, less memory suffices,
140 * but I don't know how much less: better be safe than sorry.
141 * @param idx A pointer to the position within @e names that will contain the
142 * name of the first child of 'u'. The position @names[idx+1] will contain the
143 * name of the second child of 'u'.
144 * @returns The code for the subtree rooted at 'u'.
145 */
146inline
147std::string assign_name
148(const graphs::rooted_tree& t, node u, std::string * const names, size_t idx)
149noexcept
150{
151 if (t.get_out_degree(u) == 0) {
152 return std::string("10");
153 }
154
155 // make childrens' names
156 const size_t begin_idx = idx;
157 for (node v : t.get_out_neighbours(u)) {
158 // make the name for v
159 names[idx] = assign_name(t,v, names, idx+1);
160 ++idx;
161 }
162 std::sort(&names[begin_idx], &names[idx]);
163
164 // join the names in a single string to make the name of vertex 'v'
165 std::string name = "1";
166 for (size_t j = begin_idx; j < idx; ++j) {
167 name += names[j];
168 }
169 name += "0";
170
171 return name;
172}
173
174inline
175bool are_full_trees_isomorphic
176(const graphs::rooted_tree& t1, const graphs::rooted_tree& t2)
177noexcept
178{
179 const auto discard = internal::fast_non_iso(t1,t2);
180 if (discard == 0) { return true; }
181 if (discard == 1) { return false; }
182
183 const uint32_t n = t1.get_num_nodes();
184 data_array<std::string> names(n);
185 const std::string name_r1 = assign_name(t1, t1.get_root(), names.data, 0);
186 const std::string name_r2 = assign_name(t2, t2.get_root(), names.data, 0);
187 return name_r1 == name_r2;
188}
189
190} // -- namespace internal
191} // -- namespace lal
Main namespace of the library.
Definition definitions.hpp:48
uint32_t node
Node type.
Definition definitions.hpp:51