Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Graph_Coloring.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
66#ifndef GRAPH_COLORING_H
67#define GRAPH_COLORING_H
68
69#include <tpl_graph.H>
70#include <tpl_array.H>
71#include <tpl_dynMapTree.H>
72#include <tpl_dynSetTree.H>
73#include <tpl_sort_utils.H>
74#include <cookie_guard.H>
75#include <ah-errors.H>
76
77namespace Aleph {
78
79namespace graph_coloring_detail {
80
81inline void *encode_color(size_t c) noexcept
82{
83 return reinterpret_cast<void *>(static_cast<uintptr_t>(c + 1));
84}
85
86inline bool is_colored(void *cookie) noexcept
87{
88 return cookie != nullptr;
89}
90
91inline size_t decode_color(void *cookie) noexcept
92{
93 return static_cast<size_t>(reinterpret_cast<uintptr_t>(cookie)) - 1;
94}
95
97{
98 size_t c = 0;
99 while (used.search(c) != nullptr)
100 ++c;
101 return c;
102}
103
110template <class GT, class SA>
113{
114 using Node = typename GT::Node;
115
116 // Ensure all nodes are present in the adjacency map with empty lists.
117 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
118 adj.insert(it.get_curr(), DynList<Node *>());
119
120 // Temporary per-node neighbor sets to deduplicate parallel arcs.
122 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
123 neighbor_sets.insert(it.get_curr(), DynSetTree<Node *>());
124
125 // Collect undirected neighbors, deduplicating parallel arcs.
126 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
127 {
128 auto *arc = it.get_curr();
129 if (not SA()(arc))
130 continue;
131 Node *u = g.get_src_node(arc);
132 Node *v = g.get_tgt_node(arc);
133 if (u == v)
134 continue;
135 neighbor_sets[u].insert(v);
136 neighbor_sets[v].insert(u);
137 }
138
139 // Materialize deduplicated neighbor sets into adjacency lists.
140 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
141 {
142 Node *u = it.get_curr();
143 auto &adj_list = adj[u];
144 auto &nbrs = neighbor_sets[u];
145 for (auto sit = nbrs.get_it(); sit.has_curr(); sit.next_ne())
146 adj_list.append(sit.get_curr());
147 }
148}
149
150template <class GT, class SA = Dft_Show_Arc<GT>>
152{
153 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
154 {
155 auto *arc = it.get_curr();
156 ah_domain_error_if(SA()(arc) and g.get_src_node(arc) == g.get_tgt_node(arc))
157 << "graph coloring does not support self-loops";
158 }
159}
160
161} // end namespace graph_coloring_detail
162
194template <class GT, class SA = Dft_Show_Arc<GT>>
196{
197 using Node = typename GT::Node;
198 using namespace graph_coloring_detail;
199
200 colors = {};
201 if (g.get_num_nodes() == 0)
202 return 0;
203
205
208
209 Cookie_Saver<GT> saver(g, true, false);
210 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
211 NODE_COOKIE(it.get_curr()) = nullptr;
212
213 size_t num_colors = 0;
214 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
215 {
216 Node *v = it.get_curr();
218
219 for (auto nit = adj[v].get_it(); nit.has_curr(); nit.next_ne())
220 if (Node *w = nit.get_curr(); is_colored(NODE_COOKIE(w)))
221 used.insert(decode_color(NODE_COOKIE(w)));
222
223 size_t c = smallest_available(used);
224 NODE_COOKIE(v) = encode_color(c);
225 colors.insert(v, c);
226
227 if (c + 1 > num_colors)
228 num_colors = c + 1;
229 }
230
231 return num_colors;
232}
233
262template <class GT, class SA = Dft_Show_Arc<GT>>
264{
265 using Node = typename GT::Node;
266 using namespace graph_coloring_detail;
267
268 colors = {};
269 if (g.get_num_nodes() == 0)
270 return 0;
271
273
276
277 Cookie_Saver<GT> saver(g, true, false);
279 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
280 {
281 Node *p = it.get_curr();
282 NODE_COOKIE(p) = nullptr;
283 nodes.append(p);
284 }
285
286 mergesort(nodes, [&adj](Node *a, Node *b)
287 {
288 return adj[a].size() > adj[b].size();
289 });
290
291 size_t num_colors = 0;
292 for (auto nit = nodes.get_it(); nit.has_curr(); nit.next_ne())
293 {
294 Node *v = nit.get_curr();
296
297 for (auto ait = adj[v].get_it(); ait.has_curr(); ait.next_ne())
298 {
299 Node *w = ait.get_curr();
300 if (is_colored(NODE_COOKIE(w)))
301 used.insert(decode_color(NODE_COOKIE(w)));
302 }
303
304 size_t c = smallest_available(used);
305 NODE_COOKIE(v) = encode_color(c);
306 colors.insert(v, c);
307
308 if (c + 1 > num_colors)
309 num_colors = c + 1;
310 }
311
312 return num_colors;
313}
314
345template <class GT, class SA = Dft_Show_Arc<GT>>
347{
348 using Node = typename GT::Node;
349 using namespace graph_coloring_detail;
350
351 colors = {};
352 if (const size_t n = g.get_num_nodes(); n == 0)
353 return 0;
354
356
359
360 Cookie_Saver<GT> saver(g, true, false);
363
364 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
365 {
366 Node *v = it.get_curr();
367 NODE_COOKIE(v) = nullptr;
370 }
371
372 size_t num_colors = 0;
373 while (not uncolored_nodes.is_empty())
374 {
375 Node *best = nullptr;
376 size_t best_sat = 0;
377 size_t best_deg = 0;
378
379 // Select best node: O(V)
380 for (auto it = uncolored_nodes.get_it(); it.has_curr(); it.next_ne())
381 {
382 Node *v = it.get_curr();
383 size_t sat = saturation_sets[v].size();
384 size_t deg = adj[v].size();
385
386 if (best == nullptr or sat > best_sat or (sat == best_sat and deg > best_deg))
387 {
388 best = v;
389 best_sat = sat;
390 best_deg = deg;
391 }
392 }
393
394 size_t c = smallest_available(saturation_sets[best]);
395 NODE_COOKIE(best) = encode_color(c);
396 colors.insert(best, c);
397 uncolored_nodes.remove(best);
398
399 if (c + 1 > num_colors)
400 num_colors = c + 1;
401
402 // Update neighbors: O(deg(best) log colors)
403 for (auto it = adj[best].get_it(); it.has_curr(); it.next_ne())
404 if (Node *w = it.get_curr(); not is_colored(NODE_COOKIE(w)))
405 saturation_sets[w].insert(c);
406 }
407
408 return num_colors;
409}
410
439template <class GT, class SA = Dft_Show_Arc<GT>>
442{
443 using Node = typename GT::Node;
444
445 // Explicit per-node check: every graph node must have a color entry.
446 // A size match alone is insufficient — the map could be keyed with
447 // foreign pointers that coincidentally match the node count.
448 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
449 if (colors.search(it.get_curr()) == nullptr)
450 return false;
451
452 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
453 {
454 auto *arc = it.get_curr();
455 if (not SA()(arc))
456 continue;
457 Node *u = g.get_src_node(arc);
458 Node *v = g.get_tgt_node(arc);
459 if (u == v)
460 return false;
461 auto *pu = colors.search(u);
462 auto *pv = colors.search(v);
463 if (pu == nullptr or pv == nullptr)
464 return false;
465 if (pu->second == pv->second)
466 return false;
467 }
468
469 return true;
470}
471
505template <class GT, class SA = Dft_Show_Arc<GT>>
507{
508 using Node = typename GT::Node;
509 using namespace graph_coloring_detail;
510
512 << "chromatic_number: graph has " << g.get_num_nodes() << " nodes (max 64)";
513
514 colors = {};
515 if (g.get_num_nodes() == 0)
516 return 0;
517
519
522
523 if (upper <= 2)
524 {
525 colors = std::move(best_colors);
526 return upper;
527 }
528
529 DynList<Node *> node_list;
530 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
531 node_list.append(it.get_curr());
532
533 const size_t n = node_list.size();
534 Array<Node *> nodes(n, nullptr);
535 size_t idx = 0;
536 for (auto it = node_list.get_it(); it.has_curr(); it.next_ne())
537 nodes[idx++] = it.get_curr();
538
540 {
541 Cookie_Saver<GT> saver(g, true, false);
542 for (size_t i = 0; i < n; ++i)
543 NODE_COOKIE(nodes[i]) = reinterpret_cast<void *>(static_cast<uintptr_t>(i));
544
545 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
546 {
547 auto *arc = it.get_curr();
548 if (not SA()(arc))
549 continue;
550 size_t i = reinterpret_cast<uintptr_t>(NODE_COOKIE(g.get_src_node(arc)));
551 size_t j = reinterpret_cast<uintptr_t>(NODE_COOKIE(g.get_tgt_node(arc)));
552 if (i != j)
553 {
554 adj_idx[i].append(j);
555 adj_idx[j].append(i);
556 }
557 }
558 }
559
560 Array<size_t> coloring(n, static_cast<size_t>(0));
561 auto reset_coloring = [&]()
562 {
563 for (size_t i = 0; i < n; ++i)
564 coloring[i] = 0;
565 };
566
567 auto try_k_coloring = [&](size_t k, auto &self, size_t node_idx) -> bool
568 {
569 if (node_idx == n)
570 return true;
571 for (size_t c = 0; c < k; ++c)
572 {
573 bool feasible = true;
574 for (auto jit = adj_idx[node_idx].get_it(); jit.has_curr(); jit.next_ne())
575 {
576 size_t j = jit.get_curr();
577 if (j < node_idx and coloring[j] == c)
578 {
579 feasible = false;
580 break;
581 }
582 }
583 if (feasible)
584 {
585 coloring[node_idx] = c;
586 if (self(k, self, node_idx + 1))
587 return true;
588 }
589 }
590 coloring[node_idx] = 0;
591 return false;
592 };
593
594 size_t lo = 2, hi = upper;
595 while (lo < hi)
596 {
597 size_t mid = (lo + hi) / 2;
600 hi = mid;
601 else
602 lo = mid + 1;
603 }
604
605 if (lo < upper)
606 {
609 colors = {};
610 for (size_t i = 0; i < n; ++i)
611 colors.insert(nodes[i], coloring[i]);
612 }
613 else
614 colors = std::move(best_colors);
615
616 return lo;
617}
618
631template <class GT, class SA = Dft_Show_Arc<GT>>
633{
634public:
642 {
644 }
645};
646
654template <class GT, class SA = Dft_Show_Arc<GT>>
656{
657public:
668};
669
677template <class GT, class SA = Dft_Show_Arc<GT>>
679{
680public:
688 {
690 }
691};
692
693} // end namespace Aleph
694
695#endif // GRAPH_COLORING_H
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
WeightedDigraph::Node Node
long double w
Definition btreepic.C:153
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
RAII guard that saves and restores graph cookies.
Functor wrapper for dsatur_coloring.
size_t operator()(const GT &g, DynMapTree< typename GT::Node *, size_t > &colors) const
Execute DSatur coloring.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
Generic key-value map implemented on top of a binary search tree.
Pair * insert(const Key &key, const Data &data)
Insert a key-value pair.
Dynamic set backed by balanced binary search trees with automatic memory management.
const size_t & size() const
Returns the cardinality of the set.
Key * insert(const Key &key)
Inserts a key into the dynamic set.
Functor wrapper for greedy_coloring.
size_t operator()(const GT &g, DynMapTree< typename GT::Node *, size_t > &colors) const
Execute greedy coloring.
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1065
Functor wrapper for welsh_powell_coloring.
size_t operator()(const GT &g, DynMapTree< typename GT::Node *, size_t > &colors) const
Execute Welsh-Powell coloring.
auto get_arc_it() const noexcept
Obtains an iterator to the arc of graph.
Definition graph-dry.H:2808
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:737
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2786
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:743
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:222
RAII guards for graph node/arc cookies.
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
size_t dsatur_coloring(const GT &g, DynMapTree< typename GT::Node *, size_t > &colors)
DSatur graph coloring (saturation-degree heuristic).
bool is_valid_coloring(const GT &g, const DynMapTree< typename GT::Node *, size_t > &colors)
Validates a graph coloring.
size_t chromatic_number(const GT &g, DynMapTree< typename GT::Node *, size_t > &colors)
Computes the exact chromatic number of a small graph.
#define NODE_COOKIE(p)
Return the node cookie
size_t greedy_coloring(const GT &g, DynMapTree< typename GT::Node *, size_t > &colors)
Greedy graph coloring in iteration order.
size_t welsh_powell_coloring(const GT &g, DynMapTree< typename GT::Node *, size_t > &colors)
Welsh-Powell graph coloring.
size_t decode_color(void *cookie) noexcept
bool is_colored(void *cookie) noexcept
void build_undirected_adj(const GT &g, DynMapTree< typename GT::Node *, DynList< typename GT::Node * > > &adj)
Internal helper to pre-build undirected adjacency lists.
size_t smallest_available(const DynSetTree< size_t > &used)
void validate_no_self_loops(const GT &g)
void * encode_color(size_t c) noexcept
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
size_t size(Node *root) noexcept
void mergesort(T *a, const long l, const long r, std::vector< T > &buf, Compare cmp)
Sort an array using merge sort with a reusable buffer.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
static int * k
Dynamic array container with automatic resizing.
Dynamic key-value map based on balanced binary search trees.
Dynamic set implementations based on balanced binary search trees.
Generic graph and digraph implementations.
Comprehensive sorting algorithms and search utilities for Aleph-w.