use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use crate::category::{Arrow, Category};
use crate::logic::axiom::Axiom;
use crate::logic::proof::{SimpleCounterexample, SimpleProof, Verdict};
use crate::ontology::meta::{Citation, Label, OntologyName};
type KindOf<C> = <<C as Category>::Morphism as Arrow>::Kind;
fn kinded_pairs<C>(kind: KindOf<C>) -> Vec<(C::Object, C::Object)>
where
C: Category,
C::Object: Clone,
C::Morphism: Arrow<Object = C::Object>,
KindOf<C>: PartialEq,
{
C::morphisms()
.into_iter()
.filter(|m| m.kind() == kind)
.map(|m| (m.source(), m.target()))
.collect()
}
fn adjacency<E: Clone + Eq + Hash>(pairs: &[(E, E)]) -> HashMap<E, Vec<E>> {
let mut map: HashMap<E, Vec<E>> = HashMap::new();
for (from, to) in pairs {
map.entry(from.clone()).or_default().push(to.clone());
}
map
}
fn reachable_from<E: Clone + Eq + Hash>(start: &E, adj: &HashMap<E, Vec<E>>) -> HashSet<E> {
let mut visited: HashSet<E> = HashSet::new();
let mut queue: VecDeque<E> = VecDeque::new();
if let Some(neighbors) = adj.get(start) {
for n in neighbors {
if visited.insert(n.clone()) {
queue.push_back(n.clone());
}
}
}
while let Some(current) = queue.pop_front() {
if let Some(neighbors) = adj.get(¤t) {
for n in neighbors {
if visited.insert(n.clone()) {
queue.push_back(n.clone());
}
}
}
}
visited
}
fn name_with_kind<K: Debug>(axiom_name: &'static str, kind: &K) -> OntologyName {
OntologyName::new(format!("{axiom_name}[{kind:?}]"))
}
fn description_with_kind<K: Debug>(axiom_name: &'static str, kind: &K) -> Label {
Label::new(format!("{axiom_name} applied to edges of kind {kind:?}"))
}
pub struct NoCyclesOnKind<C: Category>
where
C::Morphism: Arrow,
{
kind: KindOf<C>,
_marker: PhantomData<C>,
}
impl<C: Category> NoCyclesOnKind<C>
where
C::Morphism: Arrow,
{
pub fn new(kind: KindOf<C>) -> Self {
Self {
kind,
_marker: PhantomData,
}
}
}
impl<C> Axiom for NoCyclesOnKind<C>
where
C: Category,
C::Object: Clone + Eq + Hash,
C::Morphism: Arrow<Object = C::Object>,
KindOf<C>: PartialEq,
{
fn verify(&self) -> Verdict {
let pairs = kinded_pairs::<C>(self.kind);
let adj = adjacency(&pairs);
if adj.keys().all(|e| !reachable_from(e, &adj).contains(e)) {
Ok(Box::new(SimpleProof::new(self.meta())))
} else {
Err(Box::new(SimpleCounterexample::new(self.meta())))
}
}
fn name(&self) -> OntologyName {
name_with_kind("NoCyclesOnKind", &self.kind)
}
fn description(&self) -> Label {
description_with_kind("NoCyclesOnKind", &self.kind)
}
fn citation(&self) -> Citation {
Citation::parse_static(
"Guarino (2009); Casati & Varzi (1999); Tarski (1941) Calculus of Relations",
)
}
}
pub struct AntisymmetricOnKind<C: Category>
where
C::Morphism: Arrow,
{
kind: KindOf<C>,
_marker: PhantomData<C>,
}
impl<C: Category> AntisymmetricOnKind<C>
where
C::Morphism: Arrow,
{
pub fn new(kind: KindOf<C>) -> Self {
Self {
kind,
_marker: PhantomData,
}
}
}
impl<C> Axiom for AntisymmetricOnKind<C>
where
C: Category,
C::Object: Clone + Eq + Hash,
C::Morphism: Arrow<Object = C::Object>,
KindOf<C>: PartialEq,
{
fn verify(&self) -> Verdict {
let pairs = kinded_pairs::<C>(self.kind);
let set: HashSet<(C::Object, C::Object)> = pairs.iter().cloned().collect();
if pairs
.iter()
.all(|(a, b)| a == b || !set.contains(&(b.clone(), a.clone())))
{
Ok(Box::new(SimpleProof::new(self.meta())))
} else {
Err(Box::new(SimpleCounterexample::new(self.meta())))
}
}
fn name(&self) -> OntologyName {
name_with_kind("AntisymmetricOnKind", &self.kind)
}
fn description(&self) -> Label {
description_with_kind("AntisymmetricOnKind", &self.kind)
}
fn citation(&self) -> Citation {
Citation::parse_static("Guarino (2009); Tarski (1941); Mac Lane (1971) partial orders")
}
}
pub struct AsymmetricOnKind<C: Category>
where
C::Morphism: Arrow,
{
kind: KindOf<C>,
_marker: PhantomData<C>,
}
impl<C: Category> AsymmetricOnKind<C>
where
C::Morphism: Arrow,
{
pub fn new(kind: KindOf<C>) -> Self {
Self {
kind,
_marker: PhantomData,
}
}
}
impl<C> Axiom for AsymmetricOnKind<C>
where
C: Category,
C::Object: Clone + Eq + Hash,
C::Morphism: Arrow<Object = C::Object>,
KindOf<C>: PartialEq,
{
fn verify(&self) -> Verdict {
let pairs = kinded_pairs::<C>(self.kind);
let set: HashSet<(C::Object, C::Object)> = pairs.iter().cloned().collect();
if pairs
.iter()
.all(|(a, b)| a != b && !set.contains(&(b.clone(), a.clone())))
{
Ok(Box::new(SimpleProof::new(self.meta())))
} else {
Err(Box::new(SimpleCounterexample::new(self.meta())))
}
}
fn name(&self) -> OntologyName {
name_with_kind("AsymmetricOnKind", &self.kind)
}
fn description(&self) -> Label {
description_with_kind("AsymmetricOnKind", &self.kind)
}
fn citation(&self) -> Citation {
Citation::parse_static(
"Lewis (1973) Causation; Reichenbach (1956) Direction of Time; Tarski (1941)",
)
}
}
pub struct SymmetricOnKind<C: Category>
where
C::Morphism: Arrow,
{
kind: KindOf<C>,
_marker: PhantomData<C>,
}
impl<C: Category> SymmetricOnKind<C>
where
C::Morphism: Arrow,
{
pub fn new(kind: KindOf<C>) -> Self {
Self {
kind,
_marker: PhantomData,
}
}
}
impl<C> Axiom for SymmetricOnKind<C>
where
C: Category,
C::Object: Clone + Eq + Hash,
C::Morphism: Arrow<Object = C::Object>,
KindOf<C>: PartialEq,
{
fn verify(&self) -> Verdict {
let pairs = kinded_pairs::<C>(self.kind);
let set: HashSet<(C::Object, C::Object)> = pairs.iter().cloned().collect();
if pairs
.iter()
.all(|(a, b)| set.contains(&(b.clone(), a.clone())))
{
Ok(Box::new(SimpleProof::new(self.meta())))
} else {
Err(Box::new(SimpleCounterexample::new(self.meta())))
}
}
fn name(&self) -> OntologyName {
name_with_kind("SymmetricOnKind", &self.kind)
}
fn description(&self) -> Label {
description_with_kind("SymmetricOnKind", &self.kind)
}
fn citation(&self) -> Citation {
Citation::parse_static(
"Aristotle Peri Hermeneias; Saussure (1916); Cruse (1986) Lexical Semantics; Tarski (1941)",
)
}
}
pub struct IrreflexiveOnKind<C: Category>
where
C::Morphism: Arrow,
{
kind: KindOf<C>,
_marker: PhantomData<C>,
}
impl<C: Category> IrreflexiveOnKind<C>
where
C::Morphism: Arrow,
{
pub fn new(kind: KindOf<C>) -> Self {
Self {
kind,
_marker: PhantomData,
}
}
}
impl<C> Axiom for IrreflexiveOnKind<C>
where
C: Category,
C::Object: Clone + Eq,
C::Morphism: Arrow<Object = C::Object>,
KindOf<C>: PartialEq,
{
fn verify(&self) -> Verdict {
if C::morphisms()
.into_iter()
.filter(|m| m.kind() == self.kind)
.all(|m| m.source() != m.target())
{
Ok(Box::new(SimpleProof::new(self.meta())))
} else {
Err(Box::new(SimpleCounterexample::new(self.meta())))
}
}
fn name(&self) -> OntologyName {
name_with_kind("IrreflexiveOnKind", &self.kind)
}
fn description(&self) -> Label {
description_with_kind("IrreflexiveOnKind", &self.kind)
}
fn citation(&self) -> Citation {
Citation::parse_static("Aristotle Peri Hermeneias; Lewis (1973); Tarski (1941)")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::category::Concept;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TestObj {
A,
B,
C,
}
impl Concept for TestObj {
fn variants() -> Vec<Self> {
vec![TestObj::A, TestObj::B, TestObj::C]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TestKind {
Identity,
Subsumption,
Opposition,
Causation,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct TestMorph {
from: TestObj,
to: TestObj,
kind: TestKind,
}
impl Arrow for TestMorph {
type Object = TestObj;
type Kind = TestKind;
fn source(&self) -> TestObj {
self.from
}
fn target(&self) -> TestObj {
self.to
}
fn kind(&self) -> TestKind {
self.kind
}
}
struct TestCat;
impl Category for TestCat {
type Object = TestObj;
type Morphism = TestMorph;
fn identity(obj: &TestObj) -> TestMorph {
TestMorph {
from: *obj,
to: *obj,
kind: TestKind::Identity,
}
}
fn compose(f: &TestMorph, g: &TestMorph) -> Option<TestMorph> {
if f.to != g.from {
return None;
}
Some(TestMorph {
from: f.from,
to: g.to,
kind: TestKind::Identity,
})
}
fn morphisms() -> Vec<TestMorph> {
vec![
TestMorph {
from: TestObj::A,
to: TestObj::A,
kind: TestKind::Identity,
},
TestMorph {
from: TestObj::B,
to: TestObj::B,
kind: TestKind::Identity,
},
TestMorph {
from: TestObj::C,
to: TestObj::C,
kind: TestKind::Identity,
},
TestMorph {
from: TestObj::A,
to: TestObj::B,
kind: TestKind::Subsumption,
},
TestMorph {
from: TestObj::B,
to: TestObj::C,
kind: TestKind::Subsumption,
},
TestMorph {
from: TestObj::A,
to: TestObj::B,
kind: TestKind::Opposition,
},
TestMorph {
from: TestObj::B,
to: TestObj::A,
kind: TestKind::Opposition,
},
TestMorph {
from: TestObj::A,
to: TestObj::B,
kind: TestKind::Causation,
},
]
}
}
fn expect_proves<A: Axiom>(axiom: A) {
match axiom.verify() {
Ok(_) => {}
Err(c) => panic!("expected proof but got counterexample: {}", c.meta().name),
}
}
fn expect_refutes<A: Axiom>(axiom: A) {
match axiom.verify() {
Err(_) => {}
Ok(p) => panic!("expected counterexample but got proof: {}", p.meta().name),
}
}
#[test]
fn no_cycles_holds_on_subsumption() {
expect_proves(NoCyclesOnKind::<TestCat>::new(TestKind::Subsumption));
}
#[test]
fn antisymmetric_holds_on_subsumption() {
expect_proves(AntisymmetricOnKind::<TestCat>::new(TestKind::Subsumption));
}
#[test]
fn symmetric_holds_on_opposition() {
expect_proves(SymmetricOnKind::<TestCat>::new(TestKind::Opposition));
}
#[test]
fn irreflexive_holds_on_opposition() {
expect_proves(IrreflexiveOnKind::<TestCat>::new(TestKind::Opposition));
}
#[test]
fn asymmetric_holds_on_causation() {
expect_proves(AsymmetricOnKind::<TestCat>::new(TestKind::Causation));
}
#[test]
fn symmetric_fails_on_causation() {
expect_refutes(SymmetricOnKind::<TestCat>::new(TestKind::Causation));
}
#[test]
fn meta_carries_kind_identifier() {
let ax = NoCyclesOnKind::<TestCat>::new(TestKind::Subsumption);
assert_eq!(ax.meta().name.as_str(), "NoCyclesOnKind[Subsumption]");
assert!(!ax.meta().citation.as_str().is_empty());
}
}