Skip to main content

problemreductions/models/graph/
monochromatic_triangle.rs

1//! Monochromatic Triangle problem implementation.
2//!
3//! Given a graph G = (V, E), determine whether the edges of G can be 2-colored
4//! (each edge assigned color 0 or 1) so that no triangle is monochromatic,
5//! i.e., no three mutually adjacent vertices have all three connecting edges
6//! the same color.
7
8use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
9use crate::topology::{Graph, SimpleGraph};
10use crate::traits::Problem;
11use crate::variant::VariantParam;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14
15inventory::submit! {
16    ProblemSchemaEntry {
17        name: "MonochromaticTriangle",
18        display_name: "Monochromatic Triangle",
19        aliases: &[],
20        dimensions: &[
21            VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
22        ],
23        module_path: module_path!(),
24        description: "2-color edges so that no triangle is monochromatic",
25        fields: &[
26            FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
27        ],
28    }
29}
30
31/// The Monochromatic Triangle problem.
32///
33/// Given a graph G = (V, E), determine whether the edges of G can be 2-colored
34/// so that no triangle (three mutually adjacent vertices) has all three edges
35/// the same color.
36///
37/// Each configuration entry corresponds to an edge (in the order returned by
38/// `graph.edges()`), with value 0 or 1 representing the two colors.
39///
40/// # Type Parameters
41///
42/// * `G` - Graph type (e.g., SimpleGraph)
43///
44/// # Example
45///
46/// ```
47/// use problemreductions::models::graph::MonochromaticTriangle;
48/// use problemreductions::topology::SimpleGraph;
49/// use problemreductions::{Problem, Solver, BruteForce};
50///
51/// // K4: complete graph on 4 vertices
52/// let graph = SimpleGraph::new(4, vec![(0,1),(0,2),(0,3),(1,2),(1,3),(2,3)]);
53/// let problem = MonochromaticTriangle::new(graph);
54///
55/// let solver = BruteForce::new();
56/// let solution = solver.find_witness(&problem);
57/// assert!(solution.is_some());
58/// ```
59#[derive(Debug, Clone, Serialize, Deserialize)]
60#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
61pub struct MonochromaticTriangle<G> {
62    /// The underlying graph.
63    graph: G,
64    /// Precomputed list of triangles, each stored as three edge indices.
65    triangles: Vec<[usize; 3]>,
66    /// Ordered edge list (mirrors `graph.edges()` order).
67    edge_list: Vec<(usize, usize)>,
68}
69
70impl<G: Graph> MonochromaticTriangle<G> {
71    /// Create a new Monochromatic Triangle instance.
72    pub fn new(graph: G) -> Self {
73        let edge_list = graph.edges();
74        // Build edge-to-index mapping: (min(u,v), max(u,v)) -> index
75        let mut edge_index: HashMap<(usize, usize), usize> = HashMap::new();
76        for (idx, &(u, v)) in edge_list.iter().enumerate() {
77            let key = if u < v { (u, v) } else { (v, u) };
78            edge_index.insert(key, idx);
79        }
80
81        // Find all triangles: for each triple (u, v, w) with u < v < w,
82        // check if all three edges exist.
83        let n = graph.num_vertices();
84        let mut triangles = Vec::new();
85        for u in 0..n {
86            for v in (u + 1)..n {
87                if !graph.has_edge(u, v) {
88                    continue;
89                }
90                for w in (v + 1)..n {
91                    if graph.has_edge(u, w) && graph.has_edge(v, w) {
92                        let e_uv = edge_index[&(u, v)];
93                        let e_uw = edge_index[&(u, w)];
94                        let e_vw = edge_index[&(v, w)];
95                        triangles.push([e_uv, e_uw, e_vw]);
96                    }
97                }
98            }
99        }
100
101        Self {
102            graph,
103            triangles,
104            edge_list,
105        }
106    }
107
108    /// Get a reference to the underlying graph.
109    pub fn graph(&self) -> &G {
110        &self.graph
111    }
112
113    /// Get the number of vertices in the underlying graph.
114    pub fn num_vertices(&self) -> usize {
115        self.graph.num_vertices()
116    }
117
118    /// Get the number of edges in the underlying graph.
119    pub fn num_edges(&self) -> usize {
120        self.graph.num_edges()
121    }
122
123    /// Get the precomputed list of triangles (as edge-index triples).
124    pub fn triangles(&self) -> &[[usize; 3]] {
125        &self.triangles
126    }
127
128    /// Get the number of triangles in the graph.
129    pub fn num_triangles(&self) -> usize {
130        self.triangles.len()
131    }
132
133    /// Get the ordered edge list.
134    pub fn edge_list(&self) -> &[(usize, usize)] {
135        &self.edge_list
136    }
137}
138
139impl<G> Problem for MonochromaticTriangle<G>
140where
141    G: Graph + VariantParam,
142{
143    const NAME: &'static str = "MonochromaticTriangle";
144    type Value = crate::types::Or;
145
146    fn variant() -> Vec<(&'static str, &'static str)> {
147        crate::variant_params![G]
148    }
149
150    fn dims(&self) -> Vec<usize> {
151        vec![2; self.edge_list.len()]
152    }
153
154    fn evaluate(&self, config: &[usize]) -> crate::types::Or {
155        crate::types::Or({
156            if config.len() != self.edge_list.len() {
157                return crate::types::Or(false);
158            }
159
160            // Check each triangle: if all three edges have the same color,
161            // the coloring is invalid.
162            for tri in &self.triangles {
163                let c0 = config[tri[0]];
164                let c1 = config[tri[1]];
165                let c2 = config[tri[2]];
166                if c0 == c1 && c1 == c2 {
167                    return crate::types::Or(false);
168                }
169            }
170
171            true
172        })
173    }
174}
175
176crate::declare_variants! {
177    default MonochromaticTriangle<SimpleGraph> => "2^num_edges",
178}
179
180#[cfg(feature = "example-db")]
181pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
182    // K4: 4 vertices, 6 edges, has a valid 2-coloring avoiding monochromatic triangles.
183    // Edges in order: (0,1),(0,2),(0,3),(1,2),(1,3),(2,3)
184    // Config [0,0,1,1,0,1]:
185    //   Triangle (0,1,2): edges 0,1,3 -> colors 0,0,1 -> not monochromatic
186    //   Triangle (0,1,3): edges 0,2,4 -> colors 0,1,0 -> not monochromatic
187    //   Triangle (0,2,3): edges 1,2,5 -> colors 0,1,1 -> not monochromatic
188    //   Triangle (1,2,3): edges 3,4,5 -> colors 1,0,1 -> not monochromatic
189    vec![crate::example_db::specs::ModelExampleSpec {
190        id: "monochromatic_triangle_simplegraph",
191        instance: Box::new(MonochromaticTriangle::new(SimpleGraph::new(
192            4,
193            vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
194        ))),
195        optimal_config: vec![0, 0, 1, 1, 0, 1],
196        optimal_value: serde_json::json!(true),
197    }]
198}
199
200#[cfg(test)]
201#[path = "../../unit_tests/models/graph/monochromatic_triangle.rs"]
202mod tests;