problemreductions/topology/
bipartite_graph.rs

1//! Bipartite graph with explicit left/right partitions.
2
3use super::graph::{Graph, SimpleGraph};
4use serde::{Deserialize, Serialize};
5
6/// Bipartite graph with explicit left/right partitions.
7///
8/// Vertices are split into left (indices `0..left_size`) and right (`0..right_size`).
9/// Edges connect left vertices to right vertices using bipartite-local coordinates.
10/// The [`Graph`] trait maps to a unified vertex space where right vertices are offset
11/// by `left_size`.
12///
13/// # Example
14///
15/// ```
16/// use problemreductions::topology::{BipartiteGraph, Graph};
17///
18/// // K_{2,2}: complete bipartite graph
19/// let g = BipartiteGraph::new(2, 2, vec![(0, 0), (0, 1), (1, 0), (1, 1)]);
20/// assert_eq!(g.num_vertices(), 4);
21/// assert_eq!(g.num_edges(), 4);
22/// assert!(g.has_edge(0, 2)); // left 0 -> right 0 (unified index 2)
23/// ```
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct BipartiteGraph {
26    left_size: usize,
27    right_size: usize,
28    /// Edges in bipartite-local coordinates: (left_index, right_index).
29    edges: Vec<(usize, usize)>,
30}
31
32impl BipartiteGraph {
33    /// Create a new bipartite graph.
34    ///
35    /// # Arguments
36    ///
37    /// * `left_size` - Number of vertices in the left partition
38    /// * `right_size` - Number of vertices in the right partition
39    /// * `edges` - Edges as `(left_index, right_index)` pairs in bipartite-local coordinates
40    ///
41    /// # Panics
42    ///
43    /// Panics if any edge references an out-of-bounds left or right vertex index.
44    pub fn new(left_size: usize, right_size: usize, edges: Vec<(usize, usize)>) -> Self {
45        for &(u, v) in &edges {
46            assert!(
47                u < left_size,
48                "left vertex {} out of bounds (left_size={})",
49                u,
50                left_size
51            );
52            assert!(
53                v < right_size,
54                "right vertex {} out of bounds (right_size={})",
55                v,
56                right_size
57            );
58        }
59        Self {
60            left_size,
61            right_size,
62            edges,
63        }
64    }
65
66    /// Returns the number of vertices in the left partition.
67    pub fn left_size(&self) -> usize {
68        self.left_size
69    }
70
71    /// Returns the number of vertices in the right partition.
72    pub fn right_size(&self) -> usize {
73        self.right_size
74    }
75
76    /// Returns the edges in bipartite-local coordinates.
77    pub fn left_edges(&self) -> &[(usize, usize)] {
78        &self.edges
79    }
80}
81
82impl Graph for BipartiteGraph {
83    const NAME: &'static str = "BipartiteGraph";
84
85    fn num_vertices(&self) -> usize {
86        self.left_size + self.right_size
87    }
88
89    fn num_edges(&self) -> usize {
90        self.edges.len()
91    }
92
93    fn edges(&self) -> Vec<(usize, usize)> {
94        self.edges
95            .iter()
96            .map(|&(u, v)| {
97                let a = u;
98                let b = self.left_size + v;
99                if a < b {
100                    (a, b)
101                } else {
102                    (b, a)
103                }
104            })
105            .collect()
106    }
107
108    fn has_edge(&self, u: usize, v: usize) -> bool {
109        let (u, v) = if u < v { (u, v) } else { (v, u) };
110        // u must be a left vertex and v must be a right vertex (in unified space)
111        if u >= self.left_size || v < self.left_size {
112            return false;
113        }
114        let local_v = v - self.left_size;
115        self.edges.contains(&(u, local_v))
116    }
117
118    fn neighbors(&self, v: usize) -> Vec<usize> {
119        if v < self.left_size {
120            // Left vertex: find all right neighbors
121            self.edges
122                .iter()
123                .filter(|(u, _)| *u == v)
124                .map(|(_, rv)| self.left_size + rv)
125                .collect()
126        } else {
127            // Right vertex: find all left neighbors
128            let local_v = v - self.left_size;
129            self.edges
130                .iter()
131                .filter(|(_, rv)| *rv == local_v)
132                .map(|(u, _)| *u)
133                .collect()
134        }
135    }
136}
137
138use crate::impl_variant_param;
139impl_variant_param!(BipartiteGraph, "graph", parent: SimpleGraph,
140    cast: |g| SimpleGraph::new(g.num_vertices(), g.edges()));
141
142#[cfg(test)]
143#[path = "../unit_tests/topology/bipartite_graph.rs"]
144mod tests;