ruvector_mincut/instance/
traits.rs

1//! Core traits for bounded-range minimum cut instances
2//!
3//! This module defines the `ProperCutInstance` trait that all bounded-range
4//! minimum cut solvers must implement. The trait provides a unified interface
5//! for maintaining minimum proper cuts under dynamic edge updates.
6//!
7//! # Overview
8//!
9//! A **proper cut instance** maintains the minimum proper cut for a graph
10//! under the assumption that the minimum cut value λ ∈ [λ_min, λ_max].
11//! This bounded assumption enables more efficient algorithms than maintaining
12//! the exact minimum cut for arbitrary λ values.
13//!
14//! # Guarantees
15//!
16//! - **Correctness**: If λ ∈ [λ_min, λ_max], the instance returns correct results
17//! - **Undefined behavior**: If λ < λ_min, behavior is undefined
18//! - **Detection**: If λ > λ_max, the instance reports `AboveRange`
19//!
20//! # Update Model
21//!
22//! Updates follow a two-phase protocol:
23//! 1. **Insert phase**: Call `apply_inserts()` with new edges
24//! 2. **Delete phase**: Call `apply_deletes()` with removed edges
25//!
26//! This ordering ensures graph connectivity is maintained during updates.
27
28use crate::graph::{VertexId, EdgeId, DynamicGraph};
29use super::witness::WitnessHandle;
30
31/// Result from a bounded-range instance query
32///
33/// Represents the outcome of querying a minimum proper cut instance.
34/// The instance either finds a cut within the bounded range [λ_min, λ_max]
35/// or determines that the minimum cut exceeds λ_max.
36#[derive(Debug, Clone)]
37pub enum InstanceResult {
38    /// Cut value is within [λ_min, λ_max], with witness
39    ///
40    /// The witness certifies that a proper cut exists with the given value.
41    /// The value is guaranteed to be in the range [λ_min, λ_max].
42    ///
43    /// # Fields
44    ///
45    /// - `value`: The cut value |δ(U)| where U is the witness set
46    /// - `witness`: A witness handle certifying the cut
47    ValueInRange {
48        /// The minimum proper cut value
49        value: u64,
50        /// Witness certifying the cut
51        witness: WitnessHandle,
52    },
53
54    /// Cut value exceeds λ_max
55    ///
56    /// The instance has detected that the minimum proper cut value
57    /// is strictly greater than λ_max. No witness is provided because
58    /// maintaining witnesses above the range is not required.
59    ///
60    /// This typically triggers a range adjustment in the outer algorithm.
61    AboveRange,
62}
63
64impl InstanceResult {
65    /// Check if result is in range
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use ruvector_mincut::instance::traits::InstanceResult;
71    /// use ruvector_mincut::instance::witness::WitnessHandle;
72    /// use roaring::RoaringBitmap;
73    ///
74    /// let witness = WitnessHandle::new(0, RoaringBitmap::from_iter([0, 1]), 5);
75    /// let result = InstanceResult::ValueInRange { value: 5, witness };
76    /// assert!(result.is_in_range());
77    ///
78    /// let result = InstanceResult::AboveRange;
79    /// assert!(!result.is_in_range());
80    /// ```
81    pub fn is_in_range(&self) -> bool {
82        matches!(self, InstanceResult::ValueInRange { .. })
83    }
84
85    /// Check if result is above range
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use ruvector_mincut::instance::traits::InstanceResult;
91    ///
92    /// let result = InstanceResult::AboveRange;
93    /// assert!(result.is_above_range());
94    /// ```
95    pub fn is_above_range(&self) -> bool {
96        matches!(self, InstanceResult::AboveRange)
97    }
98
99    /// Get the cut value if in range
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// use ruvector_mincut::instance::traits::InstanceResult;
105    /// use ruvector_mincut::instance::witness::WitnessHandle;
106    /// use roaring::RoaringBitmap;
107    ///
108    /// let witness = WitnessHandle::new(0, RoaringBitmap::from_iter([0]), 7);
109    /// let result = InstanceResult::ValueInRange { value: 7, witness };
110    /// assert_eq!(result.value(), Some(7));
111    ///
112    /// let result = InstanceResult::AboveRange;
113    /// assert_eq!(result.value(), None);
114    /// ```
115    pub fn value(&self) -> Option<u64> {
116        match self {
117            InstanceResult::ValueInRange { value, .. } => Some(*value),
118            InstanceResult::AboveRange => None,
119        }
120    }
121
122    /// Get the witness if in range
123    ///
124    /// # Examples
125    ///
126    /// ```
127    /// use ruvector_mincut::instance::traits::InstanceResult;
128    /// use ruvector_mincut::instance::witness::WitnessHandle;
129    /// use roaring::RoaringBitmap;
130    ///
131    /// let witness = WitnessHandle::new(0, RoaringBitmap::from_iter([0]), 7);
132    /// let result = InstanceResult::ValueInRange { value: 7, witness: witness.clone() };
133    /// assert!(result.witness().is_some());
134    ///
135    /// let result = InstanceResult::AboveRange;
136    /// assert!(result.witness().is_none());
137    /// ```
138    pub fn witness(&self) -> Option<&WitnessHandle> {
139        match self {
140            InstanceResult::ValueInRange { witness, .. } => Some(witness),
141            InstanceResult::AboveRange => None,
142        }
143    }
144}
145
146/// A bounded-range proper cut instance
147///
148/// This trait defines the interface for maintaining minimum proper cuts
149/// over a dynamic graph, assuming the cut value λ remains within a
150/// bounded range [λ_min, λ_max].
151///
152/// # Proper Cuts
153///
154/// A **proper cut** is a partition (U, V \ U) where both U and V \ U
155/// induce connected subgraphs. This is stricter than a general cut.
156///
157/// # Bounded Range Assumption
158///
159/// The instance assumes λ ∈ [λ_min, λ_max]:
160/// - If λ < λ_min: Undefined behavior
161/// - If λ ∈ [λ_min, λ_max]: Returns `ValueInRange` with witness
162/// - If λ > λ_max: Returns `AboveRange`
163///
164/// # Update Protocol
165///
166/// Updates must follow this order:
167/// 1. Call `apply_inserts()` with batch of insertions
168/// 2. Call `apply_deletes()` with batch of deletions
169/// 3. Call `query()` to get updated result
170///
171/// # Thread Safety
172///
173/// Implementations must be `Send + Sync` for use in parallel algorithms.
174pub trait ProperCutInstance: Send + Sync {
175    /// Initialize instance on graph with given bounds
176    ///
177    /// Creates a new instance that maintains minimum proper cuts
178    /// for the given graph, assuming λ ∈ [λ_min, λ_max].
179    ///
180    /// # Arguments
181    ///
182    /// * `graph` - The dynamic graph to operate on
183    /// * `lambda_min` - Minimum bound on the cut value
184    /// * `lambda_max` - Maximum bound on the cut value
185    ///
186    /// # Panics
187    ///
188    /// May panic if λ_min > λ_max or if the graph is invalid.
189    fn init(graph: &DynamicGraph, lambda_min: u64, lambda_max: u64) -> Self
190    where
191        Self: Sized;
192
193    /// Apply batch of edge insertions
194    ///
195    /// Inserts a batch of edges into the maintained structure.
196    /// Must be called **before** `apply_deletes()` in each update round.
197    ///
198    /// # Arguments
199    ///
200    /// * `edges` - Slice of (edge_id, source, target) tuples to insert
201    fn apply_inserts(&mut self, edges: &[(EdgeId, VertexId, VertexId)]);
202
203    /// Apply batch of edge deletions
204    ///
205    /// Deletes a batch of edges from the maintained structure.
206    /// Must be called **after** `apply_inserts()` in each update round.
207    ///
208    /// # Arguments
209    ///
210    /// * `edges` - Slice of (edge_id, source, target) tuples to delete
211    fn apply_deletes(&mut self, edges: &[(EdgeId, VertexId, VertexId)]);
212
213    /// Query current minimum proper cut
214    ///
215    /// Returns the current minimum proper cut value and witness,
216    /// or indicates that the cut value exceeds the maximum bound.
217    ///
218    /// # Returns
219    ///
220    /// - `ValueInRange { value, witness }` if λ ∈ [λ_min, λ_max]
221    /// - `AboveRange` if λ > λ_max
222    ///
223    /// # Complexity
224    ///
225    /// Typically O(1) to O(log n) depending on the data structure.
226    fn query(&mut self) -> InstanceResult;
227
228    /// Get the lambda bounds for this instance
229    ///
230    /// Returns the [λ_min, λ_max] bounds this instance was initialized with.
231    ///
232    /// # Returns
233    ///
234    /// A tuple (λ_min, λ_max)
235    fn bounds(&self) -> (u64, u64);
236}