{-# LANGUAGE CPP #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} module Data.Semilattice ( type (-) -- * Join semilattices , JoinSemilattice , BoundedJoinSemilattice , Join(..) , bottom , (∨) , join , joinWith , join1 , joinWith1 -- * Meet semilattices , MeetSemilattice , BoundedMeetSemilattice , Meet(..) , top , (∧) , meet , meetWith , meet1 , meetWith1 -- * Lattices , LatticeLaw , BoundedLatticeLaw , BoundedLattice , LowerBoundedLattice , UpperBoundedLattice , Lattice , glb , glbWith , lub , lubWith , eval , evalWith , eval1 , evalWith1 , cross , cross1 ) where import Control.Applicative import Data.Bool import Data.Either import Data.Fixed import Data.Foldable import Data.Functor.Apply import Data.Int import Data.Maybe import Data.Ord (Ord) import Data.Prd import Data.Semigroup.Foldable import Data.Semigroup.Join import Data.Semigroup.Meet import Data.Word import Numeric.Natural import Prelude hiding (Ord(..), Fractional(..),Num(..)) import qualified Data.IntMap as IntMap import qualified Data.IntSet as IntSet import qualified Data.Map as Map import qualified Data.Set as Set {- --(a ∧ b) ⊗ c = (a ⊗ c) ∧ (b ⊗ c), c ⊗ (a ∧ b) = (c ⊗ a) ∧ (c ⊗ b) -- (meet x y) ∧ z = x ∧ z `meet` y ∧ z -- idempotent sup dioids ? complete (join-semi)lattices derived from <=? --connr-distributivity (the group (E\{ε}, ⊗) is therefore reticulated) -- -- mon zero = const Nothing -- bounded meet semilattice -- need the codistributive property & absorbtion & commutativity If E is a distributive lattice, then (E, ∨, ∧) is a doublyidempotent dioid, the order relation (canonical) of the dioid being defined as: a ≤ b ⇔ a ∨ b = b. Conversely, let (E, ⊕, ⊗) be a doubly-idempotent dioid for which ≤, the canonical order relation relative to the law ⊕ is also a canonical order relation for ⊗: x ≤ y ⇔ x ⊗ y = x. Then E is a distributive lattice. -} -- Lattice types type LatticeLaw a = (JoinSemilattice a, MeetSemilattice a) type BoundedLatticeLaw a = (BoundedJoinSemilattice a, BoundedMeetSemilattice a) type BoundedLattice a = (Lattice a, BoundedLatticeLaw a) type LowerBoundedLattice a = (Lattice a, (Join-Monoid) a) type UpperBoundedLattice a = (Lattice a, (Meet-Monoid) a) type BoundedJoinSemilattice a = (JoinSemilattice a, (Join-Monoid) a) type BoundedMeetSemilattice a = (MeetSemilattice a, (Meet-Monoid) a) -- | Lattices. -- -- A lattice is a partially ordered set in which every two elements have a unique join -- (least upper bound or supremum) and a unique meet (greatest lower bound or infimum). -- -- /Neutrality/ -- -- @ -- x '∨' 'minimal' = x -- x '∧' 'maximal' = x -- @ -- -- /Associativity/ -- -- @ -- x '∨' (y '∨' z) = (x '∨' y) '∨' z -- x '∧' (y '∧' z) = (x '∧' y) '∧' z -- @ -- -- /Commutativity/ -- -- @ -- x '∨' y = y '∨' x -- x '∧' y = y '∧' x -- @ -- -- /Idempotency/ -- -- @ -- x '∨' x = x -- x '∧' x = x -- @ -- -- /Absorption/ -- -- @ -- (x '∨' y) '∧' y = y -- (x '∧' y) '∨' y = y -- @ -- -- See and . -- -- Note that distributivity is _not_ a requirement for a lattice, -- however distributive lattices are idempotent, commutative dioids. -- class LatticeLaw a => Lattice a -- | Birkhoff's self-dual < https://en.wikipedia.org/wiki/Median_algebra ternary median > operation. -- -- If the lattice is distributive then 'glb' has the following properties. -- -- @ -- 'glb' x y y = y -- 'glb' x y z = 'glb' z x y -- 'glb' x y z = 'glb' x z y -- 'glb' ('glb' x w y) w z = 'glb' x w ('glb' y w z) -- @ -- -- >>> glb 1 2 3 :: Int -- 2 -- >>> glb (fromList [1..3]) (fromList [3..5]) (fromList [5..7]) :: Set Int -- fromList [3,5] -- -- See 'Data.Semilattice.Property'. -- glb :: Lattice a => a -> a -> a -> a glb = glbWith id -- | -- >>> glbWith N5 1 9 7 -- N5 {fromN5 = 7.0} -- >>> glbWith N5 1 9 (0/0) -- N5 {fromN5 = 9.0} glbWith :: Lattice r => (a -> r) -> a -> a -> a -> r glbWith f x y z = (f x ∨ f y) ∧ (f y ∨ f z) ∧ (f z ∨ f x) -- | The order dual of 'glb'. -- lub :: Lattice a => a -> a -> a -> a lub = lubWith id -- | -- >>> lubWith N5 1 9 7 -- N5 {fromN5 = 7.0} -- >>> lubWith N5 1 9 (0/0) -- N5 {fromN5 = 1.0} lubWith :: Lattice r => (a -> r) -> a -> a -> a -> r lubWith f x y z = (f x ∧ f y) ∨ (f y ∧ f z) ∨ (f z ∧ f x) -- @ 'join' :: 'Lattice' a => 'Minimal' a => 'Set' a -> a @ -- join :: (Join-Monoid) a => Lattice a => Foldable f => f a -> a join = joinWith id -- >>> joinWith Just [1..5 :: Int] -- Just 5 -- >>> joinWith N5 [1,5,0/0] -- N5 {fromN5 = Infinity} -- >>> joinWith MaxMin $ [IntSet.fromList [1..5], IntSet.fromList [2..4]] -- MaxMin {unMaxMin = fromList [2,3,4]} joinWith :: (Join-Monoid) a => Foldable t => (b -> a) -> t b -> a joinWith f = foldr' ((∨) . f) bottom {-# INLINE joinWith #-} meet :: (Meet-Monoid) a => Lattice a => Foldable f => f a -> a meet = meetWith id -- | Fold over a collection using the multiplicative operation of an arbitrary semiring. -- -- @ -- 'meet' f = 'Data.foldr'' ((*) . f) 'top' -- @ -- -- -- >>> meetWith Just [1..5 :: Int] -- Just 1 -- >>> meetWith N5 [1,5,0/0] -- N5 {fromN5 = -Infinity} meetWith :: (Meet-Monoid) a => Foldable t => (b -> a) -> t b -> a meetWith f = foldr' ((∧) . f) top {-# INLINE meetWith #-} -- | The join of a list of join-semilattice elements (of length at least top) join1 :: Lattice a => Foldable1 f => f a -> a join1 = joinWith1 id -- | Fold over a non-empty collection using the join operation of an arbitrary join semilattice. -- joinWith1 :: Foldable1 t => Lattice a => (b -> a) -> t b -> a joinWith1 f = unJoin . foldMap1 (Join . f) {-# INLINE joinWith1 #-} -- | The meet of a list of meet-semilattice elements (of length at least top) meet1 :: Lattice a => Foldable1 f => f a -> a meet1 = meetWith1 id -- | Fold over a non-empty collection using the multiplicative operation of a semiring. -- -- As the collection is non-empty this does not require a distinct multiplicative unit: -- -- >>> meetWith1 Just $ 1 :| [2..5 :: Int] -- Just 120 -- >>> meetWith1 First $ 1 :| [2..(5 :: Int)] -- First {getFirst = 15} -- >>> meetWith1 First $ Nothing :| [Just (5 :: Int), Just 6, Nothing] -- First {getFirst = Just 11} -- meetWith1 :: Foldable1 t => Lattice a => (b -> a) -> t b -> a meetWith1 f = unMeet . foldMap1 (Meet . f) {-# INLINE meetWith1 #-} -- | Evaluate a lattice expression. -- -- @ (a11 ∧ .. ∧ a1m) ∨ (a21 ∧ .. ∧ a2n) ∨ ... @ -- -- >>> eval [[1, 2], [3, 4, 5], [6, 7 :: Int]] -- 1 * 2 + 3 * 4 -- 14 -- >>> eval $ sequence [[1, 2], [3, 4 :: Int]] -- 1 + 2 * 3 + 4 -- 21 -- eval :: BoundedLattice a => Functor f => Foldable f => Foldable g => f (g a) -> a eval = join . fmap meet -- >>> evalWith Max [[1..4 :: Int], [0..2 :: Int]] -- Max {getMax = 24} evalWith :: BoundedLattice r => Functor f => Functor g => Foldable f => Foldable g => (a -> r) -> f (g a) -> r evalWith f = join . fmap meet . (fmap . fmap) f eval1 :: Lattice a => Functor f => Foldable1 f => Foldable1 g => f (g a) -> a eval1 = join1 . fmap meet1 -- >>> evalWith1 (Max . Down) $ (1 :| [2..5 :: Int]) :| [-5 :| [2..5 :: Int]] -- Max {getMax = Down 9} -- >>> evalWith1 Max $ (1 :| [2..5 :: Int]) :| [-5 :| [2..5 :: Int]] -- Max {getMax = 15} -- evalWith1 :: Lattice r => Functor f => Functor g => Foldable1 f => Foldable1 g => (a -> r) -> f (g a) -> r evalWith1 f = join1 . fmap meet1 . (fmap . fmap) f -- | Cross-multiply two collections. -- -- >>> cross [1,3,5 :: Int] [2,4] -- 4 -- -- >>> cross [1,2,3 :: Int] [] -- -9223372036854775808 -- cross :: Foldable f => Applicative f => LowerBoundedLattice a => f a -> f a -> a cross a b = join $ liftA2 (∧) a b {-# INLINE cross #-} -- | Cross-multiply two non-empty collections. -- cross1 :: Foldable1 f => Apply f => Lattice a => f a -> f a -> a cross1 a b = join1 $ liftF2 (∧) a b {-# INLINE cross1 #-} -- Lattices instance Lattice () instance Lattice Bool instance Lattice Word instance Lattice Word8 instance Lattice Word16 instance Lattice Word32 instance Lattice Word64 instance Lattice Natural instance Lattice Int instance Lattice Int8 instance Lattice Int16 instance Lattice Int32 instance Lattice Int64 instance Lattice Integer instance Lattice Uni instance Lattice Deci instance Lattice Centi instance Lattice Milli instance Lattice Micro instance Lattice Nano instance Lattice Pico instance Lattice a => Lattice (Down a) instance (Lattice a, Lattice b) => Lattice (Either a b) instance Lattice a => Lattice (Maybe a) instance Lattice a => Lattice (IntMap.IntMap a) instance Lattice IntSet.IntSet instance Ord a => Lattice (Set.Set a) instance (Ord k, Lattice a) => Lattice (Map.Map k a)