use super::object::Hypergraph;
use crate::array::{Array, ArrayKind, NaturalArray};
use crate::category::Arrow;
use crate::finite_function::FiniteFunction;
use crate::indexed_coproduct::IndexedCoproduct;
use crate::strict::graph::{
node_adjacency, node_adjacency_from_incidence, sparse_relative_indegree,
};
use core::fmt::Debug;
use num_traits::{One, Zero};
fn successors<K: ArrayKind>(
adjacency: &IndexedCoproduct<K, FiniteFunction<K>>,
frontier: &K::Index,
) -> K::Index
where
K::Type<K::I>: NaturalArray<K>,
{
if frontier.is_empty() {
return K::Index::empty();
}
let f = FiniteFunction::new(frontier.clone(), adjacency.len()).unwrap();
let (g, _) = sparse_relative_indegree(adjacency, &f);
g.table
}
fn filter_unvisited<K: ArrayKind>(visited: &K::Index, candidates: &K::Index) -> K::Index
where
K::Type<K::I>: NaturalArray<K>,
{
if candidates.is_empty() {
return K::Index::empty();
}
let visited_on_candidates = visited.gather(candidates.get_range(..));
let unvisited_ix = visited_on_candidates.zero();
candidates.gather(unvisited_ix.get_range(..))
}
#[derive(Debug)]
pub enum InvalidHypergraphArrow {
TypeMismatchW,
TypeMismatchX,
NotNaturalW,
NotNaturalX,
NotNaturalS,
NotNaturalT,
}
pub(crate) fn validate_hypergraph_morphism<K: ArrayKind, O, A>(
source: &Hypergraph<K, O, A>,
target: &Hypergraph<K, O, A>,
w: &FiniteFunction<K>,
x: &FiniteFunction<K>,
) -> Result<(), InvalidHypergraphArrow>
where
K::Type<K::I>: NaturalArray<K>,
K::Type<O>: Array<K, O> + PartialEq,
K::Type<A>: Array<K, A> + PartialEq,
{
if w.source() != source.w.len() || w.target() != target.w.len() {
return Err(InvalidHypergraphArrow::TypeMismatchW);
}
if x.source() != source.x.len() || x.target() != target.x.len() {
return Err(InvalidHypergraphArrow::TypeMismatchX);
}
let composed_w = (w >> &target.w).ok_or(InvalidHypergraphArrow::TypeMismatchW)?;
if source.w != composed_w {
return Err(InvalidHypergraphArrow::NotNaturalW);
}
let composed_x = (x >> &target.x).ok_or(InvalidHypergraphArrow::TypeMismatchX)?;
if source.x != composed_x {
return Err(InvalidHypergraphArrow::NotNaturalX);
}
let s_lhs = source
.s
.map_values(w)
.ok_or(InvalidHypergraphArrow::NotNaturalS)?;
let s_rhs = target
.s
.map_indexes(x)
.ok_or(InvalidHypergraphArrow::NotNaturalS)?;
if s_lhs != s_rhs {
return Err(InvalidHypergraphArrow::NotNaturalS);
}
let t_lhs = source
.t
.map_values(w)
.ok_or(InvalidHypergraphArrow::NotNaturalT)?;
let t_rhs = target
.t
.map_indexes(x)
.ok_or(InvalidHypergraphArrow::NotNaturalT)?;
if t_lhs != t_rhs {
return Err(InvalidHypergraphArrow::NotNaturalT);
}
Ok(())
}
pub struct HypergraphArrow<K: ArrayKind, O, A> {
pub source: Hypergraph<K, O, A>,
pub target: Hypergraph<K, O, A>,
pub w: FiniteFunction<K>,
pub x: FiniteFunction<K>,
}
pub(crate) fn is_convex_subgraph_morphism<K: ArrayKind, O, A>(
source: &Hypergraph<K, O, A>,
target: &Hypergraph<K, O, A>,
w: &FiniteFunction<K>,
x: &FiniteFunction<K>,
) -> bool
where
K::Type<K::I>: NaturalArray<K>,
K::Type<O>: Array<K, O> + PartialEq,
K::Type<A>: Array<K, A> + PartialEq,
{
if validate_hypergraph_morphism(source, target, w, x).is_err() {
return false;
}
if !w.is_injective() || !x.is_injective() {
return false;
}
let g = target;
let n_nodes = g.w.len();
let n_edges = g.x.len();
let mut edge_mask = K::Index::fill(K::I::zero(), n_edges.clone());
edge_mask.scatter_assign_constant(&x.table, K::I::one());
let outside_edge_ix = edge_mask.zero();
let outside_edges = if let Some(e) = FiniteFunction::new(outside_edge_ix, n_edges) {
e
} else {
return false;
};
let s_in = if let Some(s) = g.s.map_indexes(x) {
s
} else {
return false;
};
let t_in = if let Some(t) = g.t.map_indexes(x) {
t
} else {
return false;
};
let adj_in = node_adjacency_from_incidence(&s_in, &t_in);
let s_out = if let Some(s) = g.s.map_indexes(&outside_edges) {
s
} else {
return false;
};
let t_out = if let Some(t) = g.t.map_indexes(&outside_edges) {
t
} else {
return false;
};
let adj_out = node_adjacency_from_incidence(&s_out, &t_out);
let adj_all = node_adjacency(g);
let mut visited0 = K::Index::fill(K::I::zero(), n_nodes.clone());
let mut visited1 = K::Index::fill(K::I::zero(), n_nodes.clone());
let mut frontier0 = w.table.clone();
let mut frontier1 = K::Index::empty();
visited0.scatter_assign_constant(&frontier0, K::I::one());
while !frontier0.is_empty() || !frontier1.is_empty() {
let next0: K::Index = successors::<K>(&adj_in, &frontier0);
let next1_from0 = successors::<K>(&adj_out, &frontier0);
let next1_from1 = successors::<K>(&adj_all, &frontier1);
let next0: K::Index = filter_unvisited::<K>(&visited0, &next0);
let next1: K::Index = {
let merged = next1_from0.concatenate(&next1_from1);
if merged.is_empty() {
K::Index::empty()
} else {
let (unique, _) = merged.sparse_bincount();
filter_unvisited::<K>(&visited1, &unique)
}
};
if next0.is_empty() && next1.is_empty() {
break;
}
visited0.scatter_assign_constant(&next0, K::I::one());
visited1.scatter_assign_constant(&next1, K::I::one());
frontier0 = next0;
frontier1 = next1;
}
let reached_selected = visited1.gather(w.table.get_range(..));
!reached_selected.max().is_some_and(|m| m >= K::I::one())
}
impl<K: ArrayKind, O, A> HypergraphArrow<K, O, A>
where
K::Type<O>: Array<K, O> + PartialEq,
K::Type<A>: Array<K, A> + PartialEq,
{
pub fn new(
source: Hypergraph<K, O, A>,
target: Hypergraph<K, O, A>,
w: FiniteFunction<K>,
x: FiniteFunction<K>,
) -> Result<Self, InvalidHypergraphArrow>
where
K::Type<K::I>: NaturalArray<K>,
{
HypergraphArrow {
source,
target,
w,
x,
}
.validate()
}
pub fn validate(self) -> Result<Self, InvalidHypergraphArrow>
where
K::Type<K::I>: NaturalArray<K>,
{
validate_hypergraph_morphism(&self.source, &self.target, &self.w, &self.x)?;
Ok(self)
}
pub fn is_monomorphism(&self) -> bool
where
K::Type<K::I>: NaturalArray<K>,
{
self.w.is_injective() && self.x.is_injective()
}
pub fn is_convex_subgraph(&self) -> bool
where
K::Type<K::I>: NaturalArray<K>,
{
is_convex_subgraph_morphism(&self.source, &self.target, &self.w, &self.x)
}
}
impl<K: ArrayKind, O: Debug, A: Debug> Debug for HypergraphArrow<K, O, A>
where
K::Index: Debug,
K::Type<K::I>: Debug,
K::Type<A>: Debug,
K::Type<O>: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HypergraphArrow")
.field("source", &self.source)
.field("target", &self.target)
.field("w", &self.w)
.field("x", &self.x)
.finish()
}
}
impl<K: ArrayKind, O: Debug, A: Debug> Clone for HypergraphArrow<K, O, A>
where
K::Type<K::I>: Clone,
K::Type<A>: Clone,
K::Type<O>: Clone,
{
fn clone(&self) -> Self {
Self {
source: self.source.clone(),
target: self.target.clone(),
w: self.w.clone(),
x: self.x.clone(),
}
}
}