Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
bipartite_example.C
Go to the documentation of this file.
1
84#include <iostream>
85#include <iomanip>
86#include <string>
87#include <vector>
88
89#include <tclap/CmdLine.h>
90
91#include <tpl_graph.H>
92#include <tpl_bipartite.H>
93
94using namespace std;
95using namespace Aleph;
96
97// Graph types
101
102// =============================================================================
103// Helper functions
104// =============================================================================
105
106void print_section(const string& title)
107{
108 cout << "\n" << string(60, '=') << "\n";
109 cout << " " << title << "\n";
110 cout << string(60, '=') << "\n\n";
111}
112
113void print_subsection(const string& title)
114{
115 cout << "\n--- " << title << " ---\n";
116}
117
119{
120 cout << label << ": {";
121 bool first = true;
122 for (auto it = nodes.get_it(); it.has_curr(); it.next())
123 {
124 if (not first) cout << ", ";
125 cout << it.get_curr()->get_info();
126 first = false;
127 }
128 cout << "}" << endl;
129}
130
132{
133 cout << "Matching (" << matching.size() << " edges):" << endl;
134 for (auto it = matching.get_it(); it.has_curr(); it.next())
135 {
136 auto arc = it.get_curr();
137 cout << " " << g.get_src_node(arc)->get_info()
138 << " -- " << g.get_tgt_node(arc)->get_info() << endl;
139 }
140}
141
142// =============================================================================
143// 1. What is a Bipartite Graph?
144// =============================================================================
145
147{
148 print_section("WHAT IS A BIPARTITE GRAPH?");
149
150 cout << "A bipartite graph has two disjoint vertex sets L and R,\n";
151 cout << "where every edge connects a vertex in L to a vertex in R.\n";
152 cout << "No edges exist within L or within R.\n\n";
153
154 // Example: Simple bipartite graph
155 print_subsection("Example: Workers and Tasks");
156
157 Graph g;
158
159 // Workers (left partition)
160 auto w1 = g.insert_node("Juan");
161 auto w2 = g.insert_node("Maria");
162 auto w3 = g.insert_node("Carlos");
163
164 // Tasks (right partition)
165 auto t1 = g.insert_node("Cocinar");
166 auto t2 = g.insert_node("Limpiar");
167 auto t3 = g.insert_node("Comprar");
168
169 // Skills (edges): who can do what
170 g.insert_arc(w1, t1, 1); // Juan can cook
171 g.insert_arc(w1, t2, 1); // Juan can clean
172 g.insert_arc(w2, t2, 1); // Maria can clean
173 g.insert_arc(w2, t3, 1); // Maria can buy
174 g.insert_arc(w3, t1, 1); // Carlos can cook
175 g.insert_arc(w3, t3, 1); // Carlos can buy
176
177 cout << "Workers: {Juan, Maria, Carlos}\n";
178 cout << "Tasks: {Cocinar, Limpiar, Comprar}\n\n";
179 cout << "Skills (edges):\n";
180 cout << " Juan -- Cocinar, Limpiar\n";
181 cout << " Maria -- Limpiar, Comprar\n";
182 cout << " Carlos -- Cocinar, Comprar\n\n";
183
184 // Compute bipartition
185 print_subsection("Compute bipartition");
186
187 DynDlist<Graph::Node*> left, right;
188
189 try
190 {
191 compute_bipartite(g, left, right);
192 cout << "Graph IS bipartite!\n\n";
193 print_partition("Left partition", left);
194 print_partition("Right partition", right);
195 }
196 catch (const exception& e)
197 {
198 cout << "Graph is NOT bipartite: " << e.what() << endl;
199 }
200}
201
202// =============================================================================
203// 2. Testing Bipartiteness
204// =============================================================================
205
207{
208 print_section("TESTING BIPARTITENESS");
209
210 cout << "A graph is bipartite ⟺ it has no odd-length cycles.\n";
211 cout << "Equivalently: it can be 2-colored.\n\n";
212
213 // Bipartite example: Even cycle
214 print_subsection("Example 1: Square (C4) - Bipartite");
215
216 Graph square;
217 auto a = square.insert_node("A");
218 auto b = square.insert_node("B");
219 auto c = square.insert_node("C");
220 auto d = square.insert_node("D");
221 square.insert_arc(a, b, 1);
222 square.insert_arc(b, c, 1);
223 square.insert_arc(c, d, 1);
224 square.insert_arc(d, a, 1);
225
226 cout << "Square: A-B-C-D-A (cycle of length 4 = even)\n";
227
229 try
230 {
231 compute_bipartite(square, l1, r1);
232 cout << "Result: BIPARTITE\n";
233 print_partition(" Red vertices", l1);
234 print_partition(" Blue vertices", r1);
235 }
236 catch (...)
237 {
238 cout << "Result: NOT bipartite\n";
239 }
240
241 // Non-bipartite example: Triangle (odd cycle)
242 print_subsection("Example 2: Triangle (C3) - NOT Bipartite");
243
244 Graph triangle;
245 auto t1 = triangle.insert_node("X");
246 auto t2 = triangle.insert_node("Y");
247 auto t3 = triangle.insert_node("Z");
248 triangle.insert_arc(t1, t2, 1);
249 triangle.insert_arc(t2, t3, 1);
250 triangle.insert_arc(t3, t1, 1);
251
252 cout << "Triangle: X-Y-Z-X (cycle of length 3 = odd)\n";
253
255 try
256 {
257 compute_bipartite(triangle, l2, r2);
258 cout << "Result: BIPARTITE\n";
259 }
260 catch (const exception& e)
261 {
262 cout << "Result: NOT bipartite\n";
263 cout << "Reason: Cannot 2-color an odd cycle!\n";
264 }
265
266 // Complete bipartite K3,3
267 print_subsection("Example 3: Complete Bipartite K3,3");
268
269 Graph k33;
270 auto u1 = k33.insert_node("U1");
271 auto u2 = k33.insert_node("U2");
272 auto u3 = k33.insert_node("U3");
273 auto v1 = k33.insert_node("V1");
274 auto v2 = k33.insert_node("V2");
275 auto v3 = k33.insert_node("V3");
276
277 // Connect all U's to all V's
278 k33.insert_arc(u1, v1, 1); k33.insert_arc(u1, v2, 1); k33.insert_arc(u1, v3, 1);
279 k33.insert_arc(u2, v1, 1); k33.insert_arc(u2, v2, 1); k33.insert_arc(u2, v3, 1);
280 k33.insert_arc(u3, v1, 1); k33.insert_arc(u3, v2, 1); k33.insert_arc(u3, v3, 1);
281
282 cout << "K3,3: Complete bipartite (all U's connected to all V's)\n";
283 cout << " 9 edges, 6 vertices\n";
284
286 try
287 {
289 cout << "Result: BIPARTITE\n";
290 print_partition(" Set U", l3);
291 print_partition(" Set V", r3);
292 }
293 catch (...)
294 {
295 cout << "Result: NOT bipartite\n";
296 }
297}
298
299// =============================================================================
300// 3. Maximum Matching (Concept)
301// =============================================================================
302
304{
305 print_section("MAXIMUM MATCHING");
306
307 cout << "A matching is a set of edges with no shared vertices.\n";
308 cout << "Maximum matching = largest possible matching.\n\n";
309
310 cout << "Application: Assign workers to tasks (one task per worker).\n\n";
311
312 // Job assignment problem
313 print_subsection("Job Assignment Problem");
314
315 cout << "Workers: Ana, Bob, Cam, Dan\n";
316 cout << "Tasks: Programar, Diseñar, Testear, Documentar\n\n";
317 cout << "Skills (edges in bipartite graph):\n";
318 cout << " Ana: Programar, Testear\n";
319 cout << " Bob: Programar, Diseñar\n";
320 cout << " Cam: Diseñar, Documentar\n";
321 cout << " Dan: Testear, Documentar\n\n";
322
323 cout << "Maximum matching algorithm:\n";
324 cout << "1. Build flow network with source -> L, R -> sink\n";
325 cout << "2. Each edge has capacity 1\n";
326 cout << "3. Maximum flow = maximum matching size\n\n";
327
328 cout << "Optimal assignment for this example:\n";
329 cout << " Ana -- Programar (or Testear)\n";
330 cout << " Bob -- Diseñar (or Programar)\n";
331 cout << " Cam -- Documentar (or Diseñar)\n";
332 cout << " Dan -- Testear (or Documentar)\n\n";
333
334 cout << "Result: All 4 workers can be assigned!\n";
335
336 // Another example with imperfect matching
337 print_subsection("Example with Imperfect Matching");
338
339 cout << "3 students, 2 courses:\n";
340 cout << " Student1 wants CourseA only\n";
341 cout << " Student2 wants CourseA only\n";
342 cout << " Student3 wants CourseB only\n\n";
343
344 cout << "Maximum matching: 2 students get assigned\n";
345 cout << " Student1 -- CourseA (or Student2)\n";
346 cout << " Student3 -- CourseB\n\n";
347
348 cout << "One student without a course (conflict over CourseA).\n";
349
350 // Demonstrate bipartition detection
351 print_subsection("Verify graph is bipartite");
352
353 Graph jobs;
354 auto ana = jobs.insert_node("Ana");
355 auto bob = jobs.insert_node("Bob");
356 auto prog = jobs.insert_node("Programar");
357 auto dise = jobs.insert_node("Diseñar");
358
359 jobs.insert_arc(ana, prog, 1);
360 jobs.insert_arc(ana, dise, 1);
361 jobs.insert_arc(bob, prog, 1);
362
364 try
365 {
367 cout << "Small graph verified as bipartite:\n";
368 print_partition(" Workers", l);
369 print_partition(" Tasks", r);
370 }
371 catch (...)
372 {
373 cout << "Graph is not bipartite\n";
374 }
375}
376
377// =============================================================================
378// 4. Practical Application: Dating Service
379// =============================================================================
380
382{
383 print_section("PRACTICAL: Dating Service Matching");
384
385 cout << "Match compatible people maximizing the number of dates.\n\n";
386
387 cout << "Compatibility graph:\n";
388 cout << " Sofia <-> Andres, Miguel\n";
389 cout << " Lucia <-> Miguel, David\n";
390 cout << " Camila <-> Andres, David\n\n";
391
392 // Verify this is bipartite
394 auto p1 = dating.insert_node("Sofia");
395 auto p2 = dating.insert_node("Lucia");
396 auto p3 = dating.insert_node("Camila");
397 auto q1 = dating.insert_node("Andres");
398 auto q2 = dating.insert_node("Miguel");
399 auto q3 = dating.insert_node("David");
400
401 dating.insert_arc(p1, q1, 1);
402 dating.insert_arc(p1, q2, 1);
403 dating.insert_arc(p2, q2, 1);
404 dating.insert_arc(p2, q3, 1);
405 dating.insert_arc(p3, q1, 1);
406 dating.insert_arc(p3, q3, 1);
407
410
411 print_partition("Group A", group_a);
412 print_partition("Group B", group_b);
413
414 cout << "\nMaximum matching analysis:\n";
415 cout << " Each person in Group A has 2 compatible matches\n";
416 cout << " Hall's condition: every subset has enough neighbors\n\n";
417
418 cout << "Optimal pairing (found via max-flow):\n";
419 cout << " Sofia <3 Miguel\n";
420 cout << " Lucia <3 David\n";
421 cout << " Camila <3 Andres\n\n";
422
423 cout << "All 3 people get a date! (Perfect matching exists)\n";
424}
425
426// =============================================================================
427// 5. Hall's Marriage Theorem
428// =============================================================================
429
431{
432 print_section("HALL'S MARRIAGE THEOREM");
433
434 cout << "Hall's Theorem: A bipartite graph G=(L,R,E) has a matching\n";
435 cout << "covering all of L if and only if for every subset S of L,\n";
436 cout << "|N(S)| >= |S| where N(S) = neighbors of S.\n\n";
437
438 cout << "In other words: Every subset of L must have 'enough' neighbors.\n\n";
439
440 // Example satisfying Hall's condition
441 print_subsection("Example satisfying Hall's condition");
442
443 cout << "Graph: Each H has 2 choices among W's\n";
444 cout << " H1 <-> W1, W2\n";
445 cout << " H2 <-> W2, W3\n";
446 cout << " H3 <-> W1, W3\n\n";
447
448 cout << "Check Hall's condition:\n";
449 cout << " |{H1}| = 1 <= |{W1, W2}| = 2 OK\n";
450 cout << " |{H2}| = 1 <= |{W2, W3}| = 2 OK\n";
451 cout << " |{H3}| = 1 <= |{W1, W3}| = 2 OK\n";
452 cout << " |{H1, H2}| = 2 <= |{W1, W2, W3}| = 3 OK\n";
453 cout << " |{H1, H3}| = 2 <= |{W1, W2, W3}| = 3 OK\n";
454 cout << " |{H2, H3}| = 2 <= |{W1, W2, W3}| = 3 OK\n";
455 cout << " |{H1, H2, H3}| = 3 <= |{W1, W2, W3}| = 3 OK\n\n";
456
457 cout << "Hall's condition SATISFIED => Perfect matching exists:\n";
458 cout << " H1 -- W2\n";
459 cout << " H2 -- W3\n";
460 cout << " H3 -- W1\n";
461
462 // Example violating Hall's condition
463 print_subsection("Example violating Hall's condition");
464
465 cout << "Graph: 3 A's but only 2 B's as neighbors\n";
466 cout << " A1, A2, A3 all <-> B1, B2 only (not B3)\n\n";
467
468 cout << "Check Hall's condition:\n";
469 cout << " |{A1, A2, A3}| = 3 but |N({A1, A2, A3})| = |{B1, B2}| = 2\n";
470 cout << " 3 > 2 => VIOLATED!\n\n";
471
472 cout << "Hall's condition VIOLATED => NO perfect matching!\n";
473 cout << "Maximum matching size = 2 (one A left unmatched)\n";
474
475 // Verify with compute_bipartite
476 print_subsection("Verify graphs are bipartite");
477
479 auto h1 = hall_ok.insert_node("H1");
480 auto h2 = hall_ok.insert_node("H2");
481 auto h3 = hall_ok.insert_node("H3");
482 auto w1 = hall_ok.insert_node("W1");
483 auto w2 = hall_ok.insert_node("W2");
484 auto w3 = hall_ok.insert_node("W3");
485
486 hall_ok.insert_arc(h1, w1, 1);
487 hall_ok.insert_arc(h1, w2, 1);
488 hall_ok.insert_arc(h2, w2, 1);
489 hall_ok.insert_arc(h2, w3, 1);
490 hall_ok.insert_arc(h3, w1, 1);
491 hall_ok.insert_arc(h3, w3, 1);
492
495 print_partition("Left (H's)", l1);
496 print_partition("Right (W's)", r1);
497}
498
499// =============================================================================
500// Main
501// =============================================================================
502
503int main(int argc, char* argv[])
504{
505 try
506 {
507 TCLAP::CmdLine cmd(
508 "Bipartite graph example for Aleph-w.\n"
509 "Demonstrates bipartition and maximum matching.",
510 ' ', "1.0"
511 );
512
513 TCLAP::ValueArg<string> sectionArg(
514 "s", "section",
515 "Run only specific section: def, test, match, dating, hall, or 'all'",
516 false, "all", "section", cmd
517 );
518
519 cmd.parse(argc, argv);
520
521 string section = sectionArg.getValue();
522
523 cout << "\n";
524 cout << "============================================================\n";
525 cout << " ALEPH-W BIPARTITE GRAPHS EXAMPLE\n";
526 cout << "============================================================\n";
527
528 if (section == "all" or section == "def")
530
531 if (section == "all" or section == "test")
532 demo_testing();
533
534 if (section == "all" or section == "match")
536
537 if (section == "all" or section == "dating")
538 demo_dating();
539
540 if (section == "all" or section == "hall")
542
543 cout << "\n" << string(60, '=') << "\n";
544 cout << "Bipartite graphs demo completed!\n";
545 cout << string(60, '=') << "\n\n";
546
547 return 0;
548 }
549 catch (TCLAP::ArgException& e)
550 {
551 cerr << "Error: " << e.error() << " for argument " << e.argId() << endl;
552 return 1;
553 }
554 catch (exception& e)
555 {
556 cerr << "Error: " << e.what() << endl;
557 return 1;
558 }
559}
560
int main()
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
void print_subsection(const string &title)
void print_section(const string &title)
void demo_matching()
void demo_testing()
void demo_halls_theorem()
void demo_dating()
void print_partition(const string &label, DynDlist< Graph::Node * > &nodes)
void print_matching(Graph &g, DynDlist< Graph::Arc * > &matching)
void demo_definition()
Dynamic doubly linked list with O(1) size and bidirectional access.
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:731
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:737
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
void compute_bipartite(const GT &g, DynDlist< typename GT::Node * > &l, DynDlist< typename GT::Node * > &r)
Computes the partition sets of a bipartite graph.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Node belonging to a graph implemented with a double linked adjacency list.
Definition tpl_graph.H:121
Bipartite graph detection and 2-coloring.
Generic graph and digraph implementations.
DynList< int > l