10void validate_commercial_license() {
17std::vector<std::vector<NodeId>>
20 std::vector<std::set<NodeId>> adj(n);
22 for (std::size_t i = 0; i < n; ++i) {
31 for (
NodeId c : children) {
37 for (std::size_t p1 = 0; p1 < parents.size(); ++p1) {
38 for (std::size_t p2 = p1 + 1; p2 < parents.size(); ++p2) {
39 adj[parents[p1]].insert(parents[p2]);
40 adj[parents[p2]].insert(parents[p1]);
45 std::vector<std::vector<NodeId>> moral_graph(n);
46 for (std::size_t i = 0; i < n; ++i) {
47 moral_graph[i].assign(adj[i].begin(), adj[i].end());
52std::unique_ptr<JunctionTree>
54 const std::string &heuristic) {
55 validate_commercial_license();
56 auto jt = std::make_unique<JunctionTree>(&graph);
63 std::vector<bool> eliminated(n,
false);
64 std::vector<std::vector<NodeId>> adj = moral_graph;
65 std::vector<std::vector<NodeId>> cliques;
67 for (std::size_t step = 0; step < n; ++step) {
68 std::size_t best_node = n;
73 std::size_t best_weight = std::numeric_limits<std::size_t>::max();
74 std::size_t best_fill = std::numeric_limits<std::size_t>::max();
75 std::size_t best_size = std::numeric_limits<std::size_t>::max();
77 for (std::size_t i = 0; i < n; ++i) {
78 if (eliminated[i])
continue;
80 std::vector<NodeId> neighbors;
81 for (
NodeId neighbor : adj[i])
82 if (!eliminated[neighbor]) neighbors.push_back(neighbor);
84 std::size_t fill_edges = 0;
85 for (std::size_t u = 0; u < neighbors.size(); ++u)
86 for (std::size_t v = u + 1; v < neighbors.size(); ++v)
87 if (std::find(adj[neighbors[u]].begin(), adj[neighbors[u]].end(),
88 neighbors[v]) == adj[neighbors[u]].end())
94 for (
NodeId nb : neighbors) {
96 if (weight > (std::numeric_limits<std::size_t>::max() / 2) / ns)
97 weight = std::numeric_limits<std::size_t>::max() / 2;
101 std::size_t clique_sz = neighbors.size() + 1;
103 if (weight < best_weight ||
104 (weight == best_weight && fill_edges < best_fill) ||
105 (weight == best_weight && fill_edges == best_fill && clique_sz < best_size)) {
106 best_weight = weight;
107 best_fill = fill_edges;
108 best_size = clique_sz;
113 eliminated[best_node] =
true;
114 std::vector<NodeId> neighbors;
115 for (
NodeId neighbor : adj[best_node]) {
116 if (!eliminated[neighbor])
117 neighbors.push_back(neighbor);
120 std::vector<NodeId> clique = neighbors;
121 clique.push_back(best_node);
122 std::sort(clique.begin(), clique.end());
123 cliques.push_back(clique);
125 for (std::size_t u = 0; u < neighbors.size(); ++u) {
126 for (std::size_t v = u + 1; v < neighbors.size(); ++v) {
127 auto nu = neighbors[u], nv = neighbors[v];
128 if (std::find(adj[nu].begin(), adj[nu].end(), nv) == adj[nu].end()) {
129 adj[nu].push_back(nv);
130 adj[nv].push_back(nu);
137 std::vector<std::vector<NodeId>> max_cliques;
138 std::sort(cliques.begin(), cliques.end(),
139 [](
const auto &a,
const auto &b) { return a.size() > b.size(); });
140 for (
const auto &clique : cliques) {
141 bool is_subset =
false;
142 for (
const auto &max_clq : max_cliques) {
143 if (std::includes(max_clq.begin(), max_clq.end(), clique.begin(),
150 max_cliques.push_back(clique);
153 for (
const auto &clique : max_cliques) {
154 std::vector<std::size_t> state_sizes;
157 jt->add_clique(clique, state_sizes);
162 std::size_t u, v, weight;
163 std::vector<NodeId> sepset;
165 std::vector<Edge> edges;
166 for (std::size_t i = 0; i < max_cliques.size(); ++i) {
167 for (std::size_t j = i + 1; j < max_cliques.size(); ++j) {
168 std::vector<NodeId> intersection;
169 std::set_intersection(max_cliques[i].begin(), max_cliques[i].end(),
170 max_cliques[j].begin(), max_cliques[j].end(),
171 std::back_inserter(intersection));
172 if (!intersection.empty()) {
173 edges.push_back({i, j, intersection.size(), intersection});
178 std::sort(edges.begin(), edges.end(),
179 [](
const Edge &a,
const Edge &b) { return a.weight > b.weight; });
181 std::vector<std::size_t> parent(max_cliques.size());
182 std::iota(parent.begin(), parent.end(), 0);
183 auto find = [&](std::size_t i) {
184 std::size_t root = i;
185 while (root != parent[root])
187 std::size_t curr = i;
188 while (curr != root) {
189 std::size_t nxt = parent[curr];
195 auto unite = [&](std::size_t i, std::size_t j) {
196 std::size_t root_i = find(i);
197 std::size_t root_j = find(j);
198 if (root_i != root_j) {
199 parent[root_i] = root_j;
205 for (
const auto &edge : edges) {
206 if (unite(edge.u, edge.v))
207 jt->add_separator(edge.u, edge.v, edge.sepset);
211 for (
auto &clq : jt->get_mutable_cliques()) {
212 clq.base_potential.tensor().fill(1.0);
216 std::vector<NodeId> family;
219 family.push_back(
static_cast<NodeId>(i));
221 for (
auto &clq : jt->get_mutable_cliques()) {
222 bool contains_family =
true;
224 if (std::find(clq.scope.begin(), clq.scope.end(), n) ==
226 contains_family =
false;
230 if (contains_family) {
231 clq.assigned_cpts.push_back(i);
Directed graphical model over discrete variables.
const std::vector< NodeId > & get_parents(NodeId id) const
Parents of a variable, in the order their edges were added.
std::size_t num_variables() const
Number of variables added so far.
const std::vector< NodeId > & get_children(NodeId id) const
Children of a variable, in the order their edges were added.
const VariableMetadata & get_variable(NodeId id) const
Metadata of a variable.
static std::vector< std::vector< NodeId > > moralize(const Graph &graph)
Moral graph as adjacency lists indexed by NodeId, each sorted ascending.
static std::unique_ptr< JunctionTree > compile(const Graph &graph, const std::string &heuristic="min_fill")
Compiles a junction tree.
bncore::JunctionTreeCompiler, which turns a Graph into the JunctionTree that the inference engine run...
std::uint32_t NodeId
Handle of a variable within one Graph.