use crate::compact::*;
use crate::parallel::*;
#[repr(C)]
pub struct AgenticMinCut {
pub coordinator: SharedCoordinator,
pub aggregator: ResultAggregator,
pub strategy: CoreStrategy,
pub num_vertices: u16,
pub num_edges: u16,
}
impl AgenticMinCut {
pub fn new() -> Self {
Self {
coordinator: SharedCoordinator::new(),
aggregator: ResultAggregator::new(),
strategy: CoreStrategy::GeometricRanges,
num_vertices: 0,
num_edges: 0,
}
}
pub fn init(&mut self, num_vertices: u16, num_edges: u16, strategy: CoreStrategy) {
self.num_vertices = num_vertices;
self.num_edges = num_edges;
self.strategy = strategy;
self.coordinator = SharedCoordinator::new();
self.aggregator = ResultAggregator::new();
}
pub fn coordinator_ptr(&self) -> *const SharedCoordinator {
&self.coordinator
}
pub fn add_result(&mut self, result: CoreResult) {
self.aggregator.add_result(result);
}
pub fn min_cut(&self) -> u16 {
self.aggregator.global_min
}
pub fn best_witness(&self) -> CompactWitness {
let best = self.aggregator.best_result();
CompactWitness {
membership: BitSet256::new(), seed: best.witness_seed,
boundary_size: best.witness_boundary,
cardinality: best.witness_cardinality,
hash: best.witness_hash,
}
}
pub fn is_complete(&self) -> bool {
self.coordinator.all_completed()
}
}
impl Default for AgenticMinCut {
fn default() -> Self {
Self::new()
}
}
#[cfg(target_arch = "wasm32")]
#[allow(missing_docs)]
pub mod ffi {
use super::*;
static mut INSTANCE: Option<AgenticMinCut> = None;
#[no_mangle]
pub extern "C" fn mincut_init(num_vertices: u16, num_edges: u16, strategy: u8) {
unsafe {
let s = match strategy {
0 => CoreStrategy::GeometricRanges,
1 => CoreStrategy::GraphPartition,
_ => CoreStrategy::WorkStealing,
};
let mut instance = AgenticMinCut::new();
instance.init(num_vertices, num_edges, s);
INSTANCE = Some(instance);
}
}
#[no_mangle]
pub extern "C" fn mincut_get_coordinator() -> *const SharedCoordinator {
unsafe {
INSTANCE
.as_ref()
.map(|i| i.coordinator_ptr())
.unwrap_or(core::ptr::null())
}
}
#[no_mangle]
pub extern "C" fn mincut_add_result(
core_id: u8,
status: u8,
min_cut: u16,
witness_hash: u16,
witness_seed: u16,
witness_cardinality: u16,
witness_boundary: u16,
) {
unsafe {
if let Some(ref mut instance) = INSTANCE {
instance.add_result(CoreResult {
core_id,
status,
min_cut,
witness_hash,
witness_seed,
witness_cardinality,
witness_boundary,
padding: [0; 4],
});
}
}
}
#[no_mangle]
pub extern "C" fn mincut_get_result() -> u16 {
unsafe { INSTANCE.as_ref().map(|i| i.min_cut()).unwrap_or(u16::MAX) }
}
#[no_mangle]
pub extern "C" fn mincut_is_complete() -> u8 {
unsafe {
INSTANCE
.as_ref()
.map(|i| i.is_complete() as u8)
.unwrap_or(0)
}
}
}
pub mod ruvector {
use super::*;
use std::collections::BTreeMap;
pub fn from_ruvector_graph(
vertices: &[u64],
edges: &[(u64, u64, f32)],
) -> (
Vec<CompactVertexId>,
Vec<(CompactVertexId, CompactVertexId, u16)>,
) {
let mut vertex_map = BTreeMap::new();
for (i, &v) in vertices.iter().enumerate() {
vertex_map.insert(v, i as CompactVertexId);
}
let compact_edges: Vec<_> = edges
.iter()
.filter_map(|&(src, tgt, weight)| {
let cs = vertex_map.get(&src)?;
let ct = vertex_map.get(&tgt)?;
let w = (weight * 100.0) as u16; Some((*cs, *ct, w))
})
.collect();
let compact_vertices: Vec<_> = (0..vertices.len() as u16).collect();
(compact_vertices, compact_edges)
}
pub fn compute_mincut(vertices: &[u64], edges: &[(u64, u64, f32)]) -> Option<(u16, Vec<u64>)> {
let (compact_v, compact_e) = from_ruvector_graph(vertices, edges);
if compact_v.len() > MAX_VERTICES_PER_CORE || compact_e.len() > 512 {
return None;
}
let coord = SharedCoordinator::new();
let mut exec = CoreExecutor::init(0, Some(&coord));
for (src, tgt, weight) in compact_e {
exec.add_edge(src, tgt, weight);
}
let result = exec.process();
if result.min_cut < u16::MAX {
let witness_vertices: Vec<u64> = vertices
.iter()
.enumerate()
.filter(|(i, _)| exec.state.best_witness.contains(*i as u16))
.map(|(_, &v)| v)
.collect();
Some((result.min_cut, witness_vertices))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agentic_mincut() {
let mut amc = AgenticMinCut::new();
amc.init(100, 200, CoreStrategy::GeometricRanges);
assert_eq!(amc.num_vertices, 100);
assert_eq!(amc.num_edges, 200);
assert!(!amc.is_complete());
}
#[test]
fn test_ruvector_conversion() {
let vertices = vec![100u64, 200, 300];
let edges = vec![(100u64, 200u64, 1.0f32), (200, 300, 1.0)];
let (cv, ce) = ruvector::from_ruvector_graph(&vertices, &edges);
assert_eq!(cv.len(), 3);
assert_eq!(ce.len(), 2);
assert_eq!(ce[0], (0, 1, 100)); }
#[test]
fn test_ruvector_mincut() {
let vertices: Vec<u64> = (0..5).collect();
let edges = vec![(0u64, 1, 1.0f32), (1, 2, 1.0), (2, 3, 1.0), (3, 4, 1.0)];
let result = ruvector::compute_mincut(&vertices, &edges);
if let Some((min_cut, _witness)) = result {
assert_eq!(min_cut, 100); }
}
}