#[allow(unused_imports)]
use alloc::{vec, vec::Vec};
use core::hash::Hash;
use core::marker::PhantomData;
use hashbrown::{HashMap, HashSet};
use super::arrow::Arrow;
use super::category::Category;
use super::entity::{Concept, FinitelyGenerated};
use super::functor::Functor;
use super::kinds::FunctorKind;
pub trait Quiver {
type Vertex: Concept;
type Edge: Arrow<Object = Self::Vertex>;
fn edges() -> Vec<Self::Edge>;
}
pub struct Path<Q: Quiver> {
source: Q::Vertex,
target: Q::Vertex,
edges: Vec<Q::Edge>,
}
impl<Q: Quiver> Clone for Path<Q> {
fn clone(&self) -> Self {
Self {
source: self.source.clone(),
target: self.target.clone(),
edges: self.edges.clone(),
}
}
}
impl<Q: Quiver> core::fmt::Debug for Path<Q> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Path")
.field("source", &self.source)
.field("target", &self.target)
.field("edges", &self.edges)
.finish()
}
}
impl<Q: Quiver> PartialEq for Path<Q> {
fn eq(&self, other: &Self) -> bool {
self.source == other.source && self.target == other.target && self.edges == other.edges
}
}
impl<Q: Quiver> Eq for Path<Q> {}
impl<Q: Quiver> Path<Q> {
pub fn empty(at: Q::Vertex) -> Self {
Self {
source: at.clone(),
target: at,
edges: Vec::new(),
}
}
pub fn edge(e: Q::Edge) -> Self {
Self {
source: e.source(),
target: e.target(),
edges: vec![e],
}
}
pub fn edges(&self) -> &[Q::Edge] {
&self.edges
}
pub fn len(&self) -> usize {
self.edges.len()
}
pub fn is_empty(&self) -> bool {
self.edges.is_empty()
}
}
impl<Q: Quiver> Arrow for Path<Q> {
type Object = Q::Vertex;
type Kind = ();
fn source(&self) -> Q::Vertex {
self.source.clone()
}
fn target(&self) -> Q::Vertex {
self.target.clone()
}
fn kind(&self) {}
}
pub struct FreeCategory<Q>(PhantomData<Q>);
impl<Q: Quiver> Category for FreeCategory<Q>
where
Q::Vertex: FinitelyGenerated,
{
type Object = Q::Vertex;
type Morphism = Path<Q>;
fn identity(obj: &Q::Vertex) -> Path<Q> {
Path::empty(obj.clone())
}
fn compose(f: &Path<Q>, g: &Path<Q>) -> Option<Path<Q>> {
if f.target != g.source {
return None;
}
let mut edges = f.edges.clone();
edges.extend(g.edges.iter().cloned());
Some(Path {
source: f.source.clone(),
target: g.target.clone(),
edges,
})
}
fn morphisms() -> Vec<Path<Q>> {
let mut ms: Vec<Path<Q>> = Q::Vertex::variants().into_iter().map(Path::empty).collect();
ms.extend(Q::edges().into_iter().map(Path::edge));
ms
}
}
pub trait QuiverInterpretation {
type Quiver: Quiver;
type Target: Category;
fn on_vertex(v: &<Self::Quiver as Quiver>::Vertex) -> <Self::Target as Category>::Object;
fn on_edge(e: &<Self::Quiver as Quiver>::Edge) -> <Self::Target as Category>::Morphism;
}
pub struct FreeExtension<I>(PhantomData<I>);
impl<I: QuiverInterpretation> Functor for FreeExtension<I>
where
<I::Quiver as Quiver>::Vertex: FinitelyGenerated,
{
type Source = FreeCategory<I::Quiver>;
type Target = I::Target;
fn map_object(v: &<I::Quiver as Quiver>::Vertex) -> <I::Target as Category>::Object {
I::on_vertex(v)
}
fn map_morphism(path: &Path<I::Quiver>) -> <I::Target as Category>::Morphism {
let mut acc = I::Target::identity(&I::on_vertex(&path.source));
for e in &path.edges {
let img = I::on_edge(e);
acc = I::Target::compose(&acc, &img)
.expect("QuiverInterpretation edge images must compose along the path");
}
acc
}
const KIND: FunctorKind = FunctorKind::Free;
crate::relationship_meta!(
"FreeExtension",
"the unique functor extending a quiver interpretation over the free category (free-forgetful universal property)",
"Mac Lane (1971) Categories for the Working Mathematician II.7"
);
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ReachabilityClosure<V: Eq + Hash + Clone> {
reachable: HashMap<V, HashMap<V, u32>>,
}
impl<V: Eq + Hash + Clone> Default for ReachabilityClosure<V> {
fn default() -> Self {
Self {
reachable: HashMap::new(),
}
}
}
impl<V: Eq + Hash + Clone> ReachabilityClosure<V> {
pub fn fold(edges: impl IntoIterator<Item = (V, V)>) -> Self {
let mut reachable: HashMap<V, HashMap<V, u32>> = HashMap::new();
for (source, target) in edges {
if source == target {
continue;
}
reachable
.entry(source)
.or_default()
.entry(target)
.or_insert(1);
}
loop {
let mut grew = false;
let sources: Vec<V> = reachable.keys().cloned().collect();
for source in &sources {
let mids: Vec<(V, u32)> = reachable
.get(source)
.map(|m| m.iter().map(|(t, &d)| (t.clone(), d)).collect())
.unwrap_or_default();
for (mid, d_source_mid) in mids {
let mid_targets: Vec<(V, u32)> = reachable
.get(&mid)
.map(|m| m.iter().map(|(t, &d)| (t.clone(), d)).collect())
.unwrap_or_default();
if mid_targets.is_empty() {
continue;
}
let set = reachable.entry(source.clone()).or_default();
for (t, d_mid_t) in mid_targets {
if &t == source {
continue;
}
let dist = d_source_mid.saturating_add(d_mid_t);
match set.get(&t) {
Some(&existing) if existing <= dist => {}
Some(_) => {
set.insert(t, dist);
grew = true;
}
None => {
set.insert(t, dist);
grew = true;
}
}
}
}
}
if !grew {
break;
}
}
Self { reachable }
}
pub fn reaches(&self, source: &V, target: &V) -> bool {
source == target
|| self
.reachable
.get(source)
.is_some_and(|m| m.contains_key(target))
}
pub fn distance(&self, source: &V, target: &V) -> Option<u32> {
if source == target {
return Some(0);
}
self.reachable
.get(source)
.and_then(|m| m.get(target))
.copied()
}
pub fn reflexive_image(&self, source: &V) -> Vec<(V, u32)> {
let mut out = vec![(source.clone(), 0u32)];
if let Some(m) = self.reachable.get(source) {
out.extend(m.iter().map(|(t, &d)| (t.clone(), d)));
}
out
}
pub fn strict_image(&self, source: &V) -> Vec<(V, u32)> {
self.reachable
.get(source)
.map(|m| m.iter().map(|(t, &d)| (t.clone(), d)).collect())
.unwrap_or_default()
}
pub fn edges_iter(&self) -> impl Iterator<Item = (V, V)> + '_ {
self.reachable.iter().flat_map(|(source, targets)| {
targets
.keys()
.map(move |target| (source.clone(), target.clone()))
})
}
pub fn meet_by<K: Ord>(&self, a: &V, b: &V, tie_key: impl Fn(&V) -> K) -> Option<V> {
let anc_a: HashSet<V> = self
.reflexive_image(a)
.into_iter()
.map(|(v, _)| v)
.collect();
self.strict_image(b)
.into_iter()
.filter(|(v, _)| anc_a.contains(v))
.min_by(|(v1, d1), (v2, d2)| d1.cmp(d2).then_with(|| tie_key(v1).cmp(&tie_key(v2))))
.map(|(v, _)| v)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::category::laws::assert_functor_laws;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum V {
A,
B,
}
impl Concept for V {}
impl FinitelyGenerated for V {
fn variants() -> Vec<Self> {
vec![V::A, V::B]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct E {
from: V,
to: V,
label: char,
}
impl Arrow for E {
type Object = V;
type Kind = ();
fn source(&self) -> V {
self.from
}
fn target(&self) -> V {
self.to
}
fn kind(&self) {}
}
struct TwoCycle;
impl Quiver for TwoCycle {
type Vertex = V;
type Edge = E;
fn edges() -> Vec<E> {
vec![
E {
from: V::A,
to: V::B,
label: 'f',
},
E {
from: V::B,
to: V::A,
label: 'g',
},
]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Reach {
from: V,
to: V,
}
impl Arrow for Reach {
type Object = V;
type Kind = ();
fn source(&self) -> V {
self.from
}
fn target(&self) -> V {
self.to
}
fn kind(&self) {}
}
struct ReachCat;
impl Category for ReachCat {
type Object = V;
type Morphism = Reach;
fn identity(o: &V) -> Reach {
Reach { from: *o, to: *o }
}
fn compose(f: &Reach, g: &Reach) -> Option<Reach> {
if f.to != g.from {
None
} else {
Some(Reach {
from: f.from,
to: g.to,
})
}
}
fn morphisms() -> Vec<Reach> {
let vs = V::variants();
vs.iter()
.flat_map(|&a| vs.iter().map(move |&b| Reach { from: a, to: b }))
.collect()
}
}
struct Collapse;
impl QuiverInterpretation for Collapse {
type Quiver = TwoCycle;
type Target = ReachCat;
fn on_vertex(v: &V) -> V {
*v
}
fn on_edge(e: &E) -> Reach {
Reach {
from: e.from,
to: e.to,
}
}
}
type Collapsed = FreeExtension<Collapse>;
#[crate::praxis_value(Verifiable)]
#[test]
fn compose_concatenates_paths() {
let f = Path::<TwoCycle>::edge(E {
from: V::A,
to: V::B,
label: 'f',
});
let g = Path::<TwoCycle>::edge(E {
from: V::B,
to: V::A,
label: 'g',
});
let fg = FreeCategory::<TwoCycle>::compose(&f, &g).expect("f then g composes");
assert_eq!(fg.len(), 2);
assert_eq!(fg.source(), V::A);
assert_eq!(fg.target(), V::A);
assert!(FreeCategory::<TwoCycle>::compose(&f, &f).is_none());
}
#[crate::praxis_value(Verifiable)]
#[test]
fn identity_is_the_empty_path() {
let id_a = FreeCategory::<TwoCycle>::identity(&V::A);
assert!(id_a.is_empty());
let f = Path::<TwoCycle>::edge(E {
from: V::A,
to: V::B,
label: 'f',
});
let id_b = FreeCategory::<TwoCycle>::identity(&V::B);
assert_eq!(
FreeCategory::<TwoCycle>::compose(&id_a, &f).as_ref(),
Some(&f)
);
assert_eq!(
FreeCategory::<TwoCycle>::compose(&f, &id_b).as_ref(),
Some(&f)
);
}
#[crate::praxis_value(Extensible)]
#[test]
fn free_extension_satisfies_functor_laws() {
assert_functor_laws::<Collapsed>();
}
#[crate::praxis_value(Verifiable)]
#[test]
fn free_extension_folds_paths_through_the_target() {
let f = Path::<TwoCycle>::edge(E {
from: V::A,
to: V::B,
label: 'f',
});
let g = Path::<TwoCycle>::edge(E {
from: V::B,
to: V::A,
label: 'g',
});
let fg = FreeCategory::<TwoCycle>::compose(&f, &g).unwrap();
assert_eq!(
Collapsed::map_morphism(&fg),
Reach {
from: V::A,
to: V::A
}
);
}
#[crate::praxis_value(Verifiable)]
#[test]
fn closure_materializes_the_transitive_image_once() {
let c = ReachabilityClosure::fold([(0u32, 1u32), (1, 2)]);
assert!(c.reaches(&0, &1));
assert!(c.reaches(&0, &2));
assert_eq!(c.distance(&0, &2), Some(2));
assert_eq!(c.distance(&0, &1), Some(1));
assert!(c.reaches(&0, &0));
assert_eq!(c.distance(&0, &0), Some(0));
assert!(!c.reaches(&2, &0));
assert_eq!(c.distance(&2, &0), None);
}
#[crate::praxis_value(Verifiable)]
#[test]
fn closure_reflexive_image_includes_self_and_descendants() {
let c = ReachabilityClosure::fold([(0u32, 1u32), (1, 2)]);
let mut img: Vec<u32> = c.reflexive_image(&0).into_iter().map(|(v, _)| v).collect();
img.sort_unstable();
assert_eq!(img, vec![0, 1, 2]); assert!(!c.reflexive_image(&0).iter().any(|(v, _)| *v == 9));
}
#[crate::praxis_value(Honest, Verifiable)]
#[test]
fn closure_meet_is_the_nearest_shared_target() {
let c = ReachabilityClosure::fold([(0u32, 2u32), (2, 3), (1, 2)]);
assert_eq!(c.meet_by(&0, &1, |v| *v), Some(2));
assert_eq!(c.meet_by(&2, &1, |v| *v), Some(2));
let d = ReachabilityClosure::fold([(0u32, 1u32), (5u32, 6u32)]);
assert_eq!(d.meet_by(&0, &5, |v| *v), None);
}
#[crate::praxis_value(Deterministic, Verifiable)]
#[test]
fn closure_of_a_closure_is_set_idempotent() {
let closed = [(0u32, 1u32), (1, 2), (0, 2)];
let c = ReachabilityClosure::fold(closed);
let generators = [(0u32, 1u32), (1, 2)];
let g = ReachabilityClosure::fold(generators);
let mut cs: Vec<u32> = c.strict_image(&0).into_iter().map(|(v, _)| v).collect();
let mut gs: Vec<u32> = g.strict_image(&0).into_iter().map(|(v, _)| v).collect();
cs.sort_unstable();
gs.sort_unstable();
assert_eq!(cs, gs, "the reachable set is idempotent under re-fold");
assert!(c.reaches(&0, &2) && g.reaches(&0, &2));
assert_eq!(c.distance(&0, &2), Some(1)); assert_eq!(g.distance(&0, &2), Some(2)); }
}