packages feed

moonlight-algebra-0.1.0.2: src-abstract/Moonlight/Algebra/Pure/Quantale.hs

-- | Quantales: monoids in the join-semilattice — 'tensor' distributes over
-- 'join' and annihilates 'bottom' — with commutative and residuated
-- refinements, and the Viterbi, Łukasiewicz and tropical carriers.
--
-- Laws: tensor is associative with unit 'tensorUnit'; tensor distributes over
-- join on both sides and @tensor a bottom = bottom = tensor bottom a@;
-- commutative quantales add @tensor a b = tensor b a@; residuated quantales
-- satisfy the Galois law @joinLeq (tensor x y) z = joinLeq x (residual y z)@.
-- 'IntegralQuantale' marks carriers whose unit is top; 'ChainQuantale' marks
-- carriers whose join is selective.
module Moonlight.Algebra.Pure.Quantale
  ( Quantale (..),
    CommutativeQuantale,
    ResiduatedQuantale (..),
    IntegralQuantale,
    ChainQuantale,
    ChainOrder (..),
    Viterbi (..),
    Lukasiewicz (..),
    Tropical (..),
    tensors,
  )
where

import Data.Kind (Constraint, Type)
import Moonlight.Algebra.Pure.Lattice
  ( BoundedJoinSemilattice (..),
    HeytingAlgebra (implies),
    JoinSemilattice (..),
    joinLeq,
  )

type Quantale :: Type -> Constraint
class BoundedJoinSemilattice a => Quantale a where
  tensor :: a -> a -> a
  tensorUnit :: a

instance Quantale () where
  tensor _ _ = ()
  tensorUnit = ()

instance Quantale Bool where
  tensor = (&&)
  tensorUnit = True

instance (Quantale left, Quantale right) => Quantale (left, right) where
  tensor (leftA, rightA) (leftB, rightB) =
    (tensor leftA leftB, tensor rightA rightB)
  tensorUnit =
    (tensorUnit, tensorUnit)

instance Quantale value => Quantale (key -> value) where
  tensor left right key =
    tensor (left key) (right key)
  tensorUnit =
    const tensorUnit

type CommutativeQuantale :: Type -> Constraint
class Quantale a => CommutativeQuantale a

instance CommutativeQuantale ()

instance CommutativeQuantale Bool

instance
  (CommutativeQuantale left, CommutativeQuantale right) =>
  CommutativeQuantale (left, right)

instance CommutativeQuantale value => CommutativeQuantale (key -> value)

type ResiduatedQuantale :: Type -> Constraint
class CommutativeQuantale a => ResiduatedQuantale a where
  residual :: a -> a -> a

instance ResiduatedQuantale () where
  residual _ _ = ()

instance ResiduatedQuantale Bool where
  residual = implies

instance
  (ResiduatedQuantale left, ResiduatedQuantale right) =>
  ResiduatedQuantale (left, right)
  where
  residual (leftA, rightA) (leftB, rightB) =
    (residual leftA leftB, residual rightA rightB)

instance ResiduatedQuantale value => ResiduatedQuantale (key -> value) where
  residual left right key =
    residual (left key) (right key)

-- | Marker law: 'tensorUnit' is the top element, so 'tensor' is two-sided
-- deflationary and valuation fixpoints converge within the simple-path bound.
type IntegralQuantale :: Type -> Constraint
class Quantale a => IntegralQuantale a

instance IntegralQuantale ()

instance IntegralQuantale Bool

instance
  (IntegralQuantale left, IntegralQuantale right) =>
  IntegralQuantale (left, right)

instance IntegralQuantale value => IntegralQuantale (key -> value)

-- | Marker law: 'join' is selective (returns one of its arguments), so a
-- single witness attains any finite join.
type ChainQuantale :: Type -> Constraint
class Quantale a => ChainQuantale a

instance ChainQuantale ()

instance ChainQuantale Bool

-- | The total order a selective 'join' induces on a chain quantale; the 'Ord'
-- instance is lawful exactly when the 'ChainQuantale' law holds.
type ChainOrder :: Type -> Type
newtype ChainOrder a = ChainOrder {getChainOrder :: a}
  deriving stock (Eq, Show)

instance (ChainQuantale a, Eq a) => Ord (ChainOrder a) where
  compare (ChainOrder left) (ChainOrder right)
    | left == right = EQ
    | joinLeq left right = LT
    | otherwise = GT

-- | Carrier discipline: values lie in the unit interval; the operations
-- preserve it.
type Viterbi :: Type -> Type
newtype Viterbi a = Viterbi {getViterbi :: a}
  deriving stock (Eq, Ord, Show)

instance Ord a => JoinSemilattice (Viterbi a) where
  join = max

instance (Ord a, Num a) => BoundedJoinSemilattice (Viterbi a) where
  bottom = Viterbi 0

instance (Ord a, Num a) => Quantale (Viterbi a) where
  tensor (Viterbi left) (Viterbi right) =
    Viterbi (left * right)
  tensorUnit = Viterbi 1

instance (Ord a, Num a) => CommutativeQuantale (Viterbi a)

instance (Ord a, Fractional a) => ResiduatedQuantale (Viterbi a) where
  residual (Viterbi left) (Viterbi right)
    | left <= right = Viterbi 1
    | otherwise = Viterbi (right / left)

instance (Ord a, Num a) => IntegralQuantale (Viterbi a)

instance (Ord a, Num a) => ChainQuantale (Viterbi a)

-- | Carrier discipline: values lie in the unit interval; the operations
-- preserve it.
type Lukasiewicz :: Type -> Type
newtype Lukasiewicz a = Lukasiewicz {getLukasiewicz :: a}
  deriving stock (Eq, Ord, Show)

instance Ord a => JoinSemilattice (Lukasiewicz a) where
  join = max

instance (Ord a, Num a) => BoundedJoinSemilattice (Lukasiewicz a) where
  bottom = Lukasiewicz 0

instance (Ord a, Num a) => Quantale (Lukasiewicz a) where
  tensor (Lukasiewicz left) (Lukasiewicz right) =
    Lukasiewicz (max 0 (left + right - 1))
  tensorUnit = Lukasiewicz 1

instance (Ord a, Num a) => CommutativeQuantale (Lukasiewicz a)

instance (Ord a, Num a) => ResiduatedQuantale (Lukasiewicz a) where
  residual (Lukasiewicz left) (Lukasiewicz right) =
    Lukasiewicz (min 1 (1 - left + right))

instance (Ord a, Num a) => IntegralQuantale (Lukasiewicz a)

instance (Ord a, Num a) => ChainQuantale (Lukasiewicz a)

-- | Min-plus costs. The lattice order is the dual of the derived 'Ord': join
-- is the numeric minimum and 'bottom' is 'TropicalInfinity'. The quantale
-- laws hold over all of @a@; only integrality ('tensorUnit' as top) and the
-- truncated 'residual' require the nonnegative cone.
type Tropical :: Type -> Type
data Tropical a
  = TropicalFinite !a
  | TropicalInfinity
  deriving stock (Eq, Ord, Show)

instance Ord a => JoinSemilattice (Tropical a) where
  join = min

instance Ord a => BoundedJoinSemilattice (Tropical a) where
  bottom = TropicalInfinity

instance (Ord a, Num a) => Quantale (Tropical a) where
  tensor TropicalInfinity _ = TropicalInfinity
  tensor _ TropicalInfinity = TropicalInfinity
  tensor (TropicalFinite left) (TropicalFinite right) =
    TropicalFinite (left + right)
  tensorUnit = TropicalFinite 0

instance (Ord a, Num a) => CommutativeQuantale (Tropical a)

instance (Ord a, Num a) => ResiduatedQuantale (Tropical a) where
  residual TropicalInfinity _ = TropicalFinite 0
  residual (TropicalFinite _) TropicalInfinity = TropicalInfinity
  residual (TropicalFinite left) (TropicalFinite right) =
    TropicalFinite (max 0 (right - left))

instance (Ord a, Num a) => ChainQuantale (Tropical a)

tensors :: (Quantale a, Foldable foldable) => foldable a -> a
tensors =
  foldl' tensor tensorUnit