Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions examples/lattice.rs
Original file line number Diff line number Diff line change
@@ -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<const N: usize> {
mask: u64,
}

impl<const N: usize> Iterator for Powerset<N> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.mask == 0 {
None
} else {
let tz = self.mask.trailing_zeros() as usize;
self.mask &= !(1 << tz);
Some(tz)
}
}
}

impl<const N: usize> ExactSizeIterator for Powerset<N> {
fn len(&self) -> usize {
self.mask.count_ones() as usize
}
}

impl<const N: usize> FromIterator<usize> for Powerset<N> {
fn from_iter<T: IntoIterator<Item = usize>>(iter: T) -> Self {
Self {
mask: iter.into_iter().map(|i| 1 << i).fold(0, |acc, x| acc | x),
}
}
}

impl<const N: usize> Display for Powerset<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set().entries(self.into_iter()).finish()
}
}

impl<const N: usize> PartialOrd for Powerset<N> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
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<const N: usize> JoinSemiLattice for Powerset<N> {
fn join(&self, other: &Self) -> Self {
Self {
mask: self.mask | other.mask,
}
}
}

impl<const N: usize> MeetSemiLattice for Powerset<N> {
fn meet(&self, other: &Self) -> Self {
Self {
mask: self.mask & other.mask,
}
}
}

impl<const N: usize> LowerBounded for Powerset<N> {
fn infimum() -> Self {
Self { mask: 0 }
}
}

impl<const N: usize> UpperBounded for Powerset<N> {
fn supremum() -> Self {
Self {
mask: !0 >> (64 - N),
}
}
}

impl<const N: usize> BooleanAlgebra for Powerset<N> {
fn complement(&self) -> Self {
Self {
mask: !self.mask & Self::supremum().mask,
}
}
}

impl<const N: usize> SymmetricDifference for Powerset<N> {
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<T: BooleanAlgebra>() {}
_assert_boolean_algebra::<P5>();

println!("All lattice examples succeeded.");
}
97 changes: 97 additions & 0 deletions src/lattice.rs
Original file line number Diff line number Diff line change
@@ -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<T: JoinSemiLattice + MeetSemiLattice> 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<T: Lattice + Ord> 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;
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::*;
Expand Down
75 changes: 75 additions & 0 deletions src/sets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,81 @@ pub trait Set: Sized + PartialEq {}
// Blanket implementation for any type that satisfies the trait bounds
impl<T: PartialEq> 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::*;
Expand Down