From 0b9a985bb50970582dd0f8ab5664b4dd29f331e4 Mon Sep 17 00:00:00 2001 From: Kyle Gullion Date: Fri, 3 Oct 2025 13:27:03 -0400 Subject: [PATCH] first pass at lattice and boolean algebra traits --- examples/lattice.rs | 138 ++++++++++++++++++++++++++++++++++++++++++++ src/lattice.rs | 97 +++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/sets.rs | 75 ++++++++++++++++++++++++ 4 files changed, 312 insertions(+) create mode 100644 examples/lattice.rs create mode 100644 src/lattice.rs diff --git a/examples/lattice.rs b/examples/lattice.rs new file mode 100644 index 0000000..332e2f1 --- /dev/null +++ b/examples/lattice.rs @@ -0,0 +1,138 @@ +use noether::lattice::{BooleanAlgebra, JoinSemiLattice, MeetSemiLattice}; +use noether::{LowerBounded, SymmetricDifference, UpperBounded}; +use std::cmp::Ordering; +use std::fmt::{self, Debug, Display}; + +/// Simple powerset implemented as a bitmask. +/// +/// This example demonstrates how a small finite powerset forms a lattice +/// under the subset order (⊆) with join = union and meet = intersection. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord)] +pub struct Powerset { + mask: u64, +} + +impl Iterator for Powerset { + type Item = usize; + fn next(&mut self) -> Option { + if self.mask == 0 { + None + } else { + let tz = self.mask.trailing_zeros() as usize; + self.mask &= !(1 << tz); + Some(tz) + } + } +} + +impl ExactSizeIterator for Powerset { + fn len(&self) -> usize { + self.mask.count_ones() as usize + } +} + +impl FromIterator for Powerset { + fn from_iter>(iter: T) -> Self { + Self { + mask: iter.into_iter().map(|i| 1 << i).fold(0, |acc, x| acc | x), + } + } +} + +impl Display for Powerset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.into_iter()).finish() + } +} + +impl PartialOrd for Powerset { + fn partial_cmp(&self, other: &Self) -> Option { + let meet = self.mask & other.mask; + match (self.mask == meet, other.mask == meet) { + (true, true) => Some(Ordering::Equal), + (true, false) => Some(Ordering::Less), + (false, true) => Some(Ordering::Greater), + (false, false) => None, + } + } +} + +impl JoinSemiLattice for Powerset { + fn join(&self, other: &Self) -> Self { + Self { + mask: self.mask | other.mask, + } + } +} + +impl MeetSemiLattice for Powerset { + fn meet(&self, other: &Self) -> Self { + Self { + mask: self.mask & other.mask, + } + } +} + +impl LowerBounded for Powerset { + fn infimum() -> Self { + Self { mask: 0 } + } +} + +impl UpperBounded for Powerset { + fn supremum() -> Self { + Self { + mask: !0 >> (64 - N), + } + } +} + +impl BooleanAlgebra for Powerset { + fn complement(&self) -> Self { + Self { + mask: !self.mask & Self::supremum().mask, + } + } +} + +impl SymmetricDifference for Powerset { + fn sym_diff(&self, other: &Self) -> Self { + Self { + mask: self.mask ^ other.mask, + } + } +} + +fn main() { + // Example in the powerset of 5 elements + type P5 = Powerset<5>; + + let a: P5 = [0, 2].into_iter().collect(); + let b = [1, 2, 4].into_iter().collect(); + + println!("a = {}", a); + println!("b = {}", b); + println!("a ∪ b = {}", a.join(&b)); + println!("a ∩ b = {}", a.meet(&b)); + println!( + "a Δ b (via join+meet/complements): {}", + a.join(&b) + .meet(&a.complement().join(&b.complement()).complement()) + ); + + // Boolean algebra laws + assert_eq!(a.join(&a.complement()), P5::supremum()); + assert_eq!(a.meet(&a.complement()), P5::infimum()); + + // Distributive lattice law check (one direction) + let c: P5 = [0, 1].into_iter().collect(); + let lhs = a.join(&b.meet(&c)); + let rhs = a.join(&b).meet(&a.join(&c)); + assert_eq!(lhs, rhs); + + // Type-level assertions (compile-time checks by trait bounds) + fn _assert_boolean_algebra() {} + _assert_boolean_algebra::(); + + println!("All lattice examples succeeded."); +} diff --git a/src/lattice.rs b/src/lattice.rs new file mode 100644 index 0000000..1fc4e91 --- /dev/null +++ b/src/lattice.rs @@ -0,0 +1,97 @@ +//! Lattice-theoretic primitives. +//! +//! This module provides the foundational traits for join/meet semi-lattices +//! and lattices. The traits are deliberately minimal: they describe the +//! operations required by the algebraic structure and leave verification of +//! laws (associativity, commutativity, absorption, distributivity, etc.) to +//! implementors and tests. +//! +//! Notation: +//! - ∨: join (least upper bound) +//! - ∧: meet (greatest lower bound) +//! - ⊥: least element (bottom) +//! - ⊤: greatest element (top) +use crate::{LowerBounded, Set, UpperBounded}; + +/// A join-semilattice is a partially ordered set in which any two elements +/// have a least upper bound (join, ∨). +/// +/// # Mathematical Definition +/// For a poset (P, ≤), a binary operation ∨ is a join if for all a, b ∈ P: +/// +/// 1. a ≤ a ∨ b and b ≤ a ∨ b (upper bound) +/// 2. if a ≤ c and b ≤ c then a ∨ b ≤ c (least such upper bound) +/// +/// # Properties +/// - Commutative: a ∨ b = b ∨ a +/// - Associative: (a ∨ b) ∨ c = a ∨ (b ∨ c) +/// - Idempotent: a ∨ a = a +pub trait JoinSemiLattice: Set + PartialOrd { + /// Compute the join (least upper bound) of `self` and `other`. + fn join(&self, other: &Self) -> Self; +} + +/// A meet-semilattice is a partially ordered set in which any two elements +/// have a greatest lower bound (meet, ∧). +/// +/// # Mathematical Definition +/// For a poset (P, ≤), a binary operation ∧ is a meet if for all a, b ∈ P: +/// +/// 1. a ∧ b ≤ a and a ∧ b ≤ b (lower bound) +/// 2. if c ≤ a and c ≤ b then c ≤ a ∧ b (greatest such lower bound) +/// +/// # Properties +/// - Commutative: a ∧ b = b ∧ a +/// - Associative: (a ∧ b) ∧ c = a ∧ (b ∧ c) +/// - Idempotent: a ∧ a = a +pub trait MeetSemiLattice: Set + PartialOrd { + /// Compute the meet (greatest lower bound) of `self` and `other`. + fn meet(&self, other: &Self) -> Self; +} + +/// A lattice is a structure that is both a join- and meet-semilattice. +/// +/// # Mathematical Definition +/// A type T is a lattice if it implements both a join and a meet satisfying +/// the usual lattice laws (absorption, associativity, commutativity, idempotence). +/// +/// # Usage +/// The blanket implementation is provided for any type that implements both +/// `JoinSemiLattice` and `MeetSemiLattice`. +pub trait Lattice: JoinSemiLattice + MeetSemiLattice {} +impl Lattice for T {} + +/// A distributive lattice is a lattice in which meet and join distribute over each other. +/// +/// # Mathematical Definition +/// A lattice (L, ∨, ∧) is distributive if for all a, b, c ∈ L: +/// +/// a ∨ (b ∧ c) = (a ∨ b) ∧ (a ∨ c) +/// a ∧ (b ∨ c) = (a ∧ b) ∨ (a ∧ c) +/// +/// # Properties +/// - Distributivity implies the lattice is well-behaved with respect to expansions +/// and factorizations of expressions involving ∨ and ∧. +/// - Many familiar lattices (e.g., powerset lattices ordered by ⊆) are distributive. +pub trait DistributiveLattice: Lattice + Ord {} +impl DistributiveLattice for T {} + +/// Boolean algebras are distributive lattices with a complement and bounds. +/// +/// # Mathematical Definition +/// A Boolean algebra is a distributive lattice (L, ∨, ∧, ⊥, ⊤) equipped with a +/// complement operation ¬ such that for all a ∈ L: +/// +/// a ∨ ¬a = ⊤ and a ∧ ¬a = ⊥ +/// +/// # Properties +/// - Every Boolean algebra is a complemented distributive lattice. +/// - In the powerset example, complement corresponds to set-theoretic complement +/// with respect to the universal set, ⊤. +/// +/// The trait requires `LowerBounded` and `UpperBounded` to ensure the presence +/// of ⊥ and ⊤ respectively. +pub trait BooleanAlgebra: DistributiveLattice + LowerBounded + UpperBounded { + /// Return the complement of `self` (logical negation / set complement). + fn complement(&self) -> Self; +} diff --git a/src/lib.rs b/src/lib.rs index 0ed4d5a..1006f5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub mod dimensions; pub mod fields; pub mod groups; +pub mod lattice; pub mod linear; pub mod operations; pub mod primitives; @@ -22,6 +23,7 @@ pub mod spaces; pub use dimensions::*; pub use fields::*; pub use groups::*; +pub use lattice::*; pub use linear::*; pub use operations::*; pub use rings::*; diff --git a/src/sets.rs b/src/sets.rs index a40fe97..2dc326a 100644 --- a/src/sets.rs +++ b/src/sets.rs @@ -31,6 +31,81 @@ pub trait Set: Sized + PartialEq {} // Blanket implementation for any type that satisfies the trait bounds impl Set for T {} +/// Trait for types that admit a distinguished least element (bottom, ⊥). +/// +/// # Mathematical Definition +/// For a partially ordered set (P, ≤), an element ⊥ ∈ P is a least element if: +/// +/// ∀ x ∈ P, ⊥ ≤ x +/// +/// When the least element exists it is the infimum of the whole set P and is +/// often denoted ⊥ (bottom). This trait expresses that the implementing type +/// provides a canonical least element for the type as a whole. +/// +/// # Properties +/// - Uniqueness: a least element (when it exists) is unique. +/// - Lattice usage: in lattices the existence of ⊥ makes the lattice lower-bounded. +/// - Not all posets have a least element -- implement this trait only when such an +/// element is defined for the type. +/// +/// # Examples +/// - For the power set P(X) ordered by ⊆, the empty set ∅ is ⊥. +/// - For bounded numeric intervals, the lower endpoint is ⊥ when present. +pub trait LowerBounded: Set + PartialOrd { + /// Return the distinguished least element (infimum / bottom) for this type. + fn infimum() -> Self; +} + +/// Trait for types that admit a distinguished greatest element (top, ⊤). +/// +/// # Mathematical Definition +/// For a partially ordered set (P, ≤), an element ⊤ ∈ P is a greatest element if: +/// +/// ∀ x ∈ P, x ≤ ⊤ +/// +/// When the greatest element exists it is the supremum of the whole set P and +/// is often denoted ⊤ (top). This trait expresses that the implementing type +/// provides a canonical greatest element for the type as a whole. +/// +/// # Properties +/// - Uniqueness: a greatest element (when it exists) is unique. +/// - Lattice usage: in lattices the existence of ⊤ makes the lattice upper-bounded. +/// - Not all posets have a greatest element -- implement this trait only when such an +/// element is defined for the type. +/// +/// # Examples +/// - For the power set P(X) ordered by ⊆, the universal set X is ⊤. +/// - For bounded numeric intervals, the upper endpoint is ⊤ when present. +pub trait UpperBounded: Set + PartialOrd { + /// Return the distinguished greatest element (supremum / top) for this type. + fn supremum() -> Self; +} + +/// Trait describing the symmetric difference operation (Δ) between two elements. +/// +/// # Mathematical Definition +/// Given two sets A and B, the symmetric difference is defined as: +/// +/// A Δ B = (A \\ B) ∪ (B \\ A) +/// +/// which is the set of elements that belong to exactly one of A or B. +/// +/// # Algebraic Properties +/// - Commutative: A Δ B = B Δ A +/// - Associative: (A Δ B) Δ C = A Δ (B Δ C) +/// - Identity: A Δ ∅ = A (the empty set ∅ acts as the identity) +/// - Self-inverse: A Δ A = ∅ (every element is its own inverse under Δ) +/// - For the power set of X, (P(X), Δ) is an abelian group isomorphic to the +/// vector space (over GF(2)) of indicator functions on X. +/// +/// Implement this trait when a type naturally supports a symmetric-difference +/// style binary operation. Implementations should preserve the above algebraic +/// laws wherever they are meaningful for the type. +pub trait SymmetricDifference: Set { + /// Compute the symmetric difference of `a` and `b`. + fn sym_diff(&self, b: &Self) -> Self; +} + #[cfg(test)] mod tests { use super::*;