group-theory-0.2.0.0: src/Data/Group.hs
{-# language CPP #-}
{-# language DerivingStrategies #-}
{-# language FlexibleInstances #-}
{-# language PackageImports #-}
{-# language PatternSynonyms #-}
{-# language Safe #-}
#if MIN_VERSION_base(4,12,0)
{-# language TypeOperators #-}
#endif
{-# language ViewPatterns #-}
-- |
-- Module : Data.Group
-- Copyright : (c) 2020 Emily Pillmore
-- License : BSD-style
--
-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>,
-- Reed Mullanix <reedmullanix@gmail.com>
-- Stability : stable
-- Portability : non-portable
--
-- This module contains definitions for 'Group' and 'Abelian',
-- along with the relevant combinators.
--
module Data.Group
( -- * Groups
-- $groups
G.Group(..)
-- * Group combinators
, minus
, gtimes
, (><)
-- ** Conjugation
, conjugate
, unconjugate
, pattern Conjugate
-- ** Elements
, pattern Inverse
, pattern IdentityElem
-- ** Abelianization
, Abelianizer(..)
, abelianize
, commutate
, pattern Abelianized
, pattern Quotiented
-- * Abelian groups
-- $abelian
, G.Abelian
) where
import Data.Bool
import "groups" Data.Group as G
import Data.Monoid
import Prelude hiding (negate, exponent)
-- $setup
--
-- >>> import qualified Prelude
-- >>> import Data.Group
-- >>> import Data.Monoid
-- >>> import Data.Semigroup
-- >>> import Data.Word
-- >>> :set -XTypeApplications
-- >>> :set -XFlexibleContexts
infixr 6 ><
-- -------------------------------------------------------------------- --
-- Group combinators
{- $groups
The typeclass of groups (types with an associative binary operation that
has an identity, and all inverses, i.e. a 'Monoid' with all inverses),
representing the structural symmetries of a mathematical object.
Instances should satisfy the following:
[Right identity] @ x '<>' 'mempty' = x@
[Left identity] @'mempty' '<>' x = x@
[Associativity] @ x '<>' (y '<>' z) = (x '<>' y) '<>' z@
[Concatenation] @ 'mconcat' = 'foldr' ('<>') 'mempty'@
[Right inverses] @ x '<>' 'invert' x = 'mempty' @
[Left inverses] @ 'invert' x '<>' x = 'mempty' @
Some types can be viewed as a group in more than one way,
e.g. both addition and multiplication on numbers.
In such cases we often define @newtype@s and make those instances
of 'Group', e.g. 'Data.Semigroup.Sum' and 'Data.Semigroup.Product'.
Often in practice such differences between addition and
multiplication-like operations matter (e.g. when defining rings), and
so, classes "additive" (the underlying operation is addition-like) and
"multiplicative" group classes are provided in vis 'Data.Group.Additive.AdditiveGroup' and
'Data.Group.Multiplicative.MultiplicativeGroup'.
Categorically, 'Group's may be viewed single-object groupoids.
-}
-- | An alias to 'pow'.
--
-- Similar to 'Data.Semigroup.stimes' from 'Data.Semigroup', but handles
-- negative powers by using 'invert' appropriately.
--
-- === __Examples:__
--
-- >>> gtimes 2 (Sum 3)
-- Sum {getSum = 6}
-- >>> gtimes (-3) (Sum 3)
-- Sum {getSum = -9}
--
gtimes :: (Group a, Integral n) => n -> a -> a
gtimes = flip pow
{-# inline gtimes #-}
-- | 'Group' subtraction.
--
-- This function denotes principled 'Group' subtraction, where
-- @a `minus` b@ translates into @a <> invert b@. This is because
-- subtraction as an operator is non-associative, but the operation
-- described in terms of addition and inversion is.
--
minus :: Group a => a -> a -> a
minus a b = a <> invert b
{-# inline minus #-}
-- | Apply @('<>')@, commuting its arguments. When the group is abelian,
-- @a <> b@ is identically @b <> a@.
--
(><) :: Group a => a -> a -> a
a >< b = b <> a
{-# inline (><) #-}
-- -------------------------------------------------------------------- --
-- Group conjugation
-- | Conjugate an element of a group by another element.
-- When the group is abelian, conjugation is the identity.
--
-- Symbolically, this is \( (g,a) \mapsto gag^{-1} \).
--
-- === __Examples__:
--
-- >>> let x = Sum (3 :: Int)
-- >>> conjugate x x
-- Sum {getSum = 3}
--
conjugate :: Group a => a -> a -> a
conjugate g a = (g <> a) `minus` g
{-# inline conjugate #-}
-- | Apply an inverse conjugate to a conjugated element.
--
-- @
-- unconjugate . conjugate = id
-- conjugate . unconjugate = id
-- @
--
-- === __Examples__:
--
-- >>> let x = Sum (3 :: Int)
-- >>> unconjugate x (conjugate x x)
-- Sum {getSum = 3}
--
unconjugate :: Group a => a -> a -> a
unconjugate g a = invert g <> a <> g
-- | Bidirectional pattern for conjugation by a group element
--
-- __Note:__ When the underlying 'Group' is abelian, this
-- pattern is the identity.
--
pattern Conjugate :: Group a => (a,a) -> (a,a)
pattern Conjugate t <- (\(g,a) -> (g, conjugate g a) -> t) where
Conjugate (g,a) = (g, unconjugate g a)
{-# complete Conjugate #-}
-- | Bidirectional pattern for inverse elements.
pattern Inverse :: (Group g) => g -> g
pattern Inverse t <- (invert -> t) where
Inverse g = invert g
-- | Bidirectional pattern for the identity element.
pattern IdentityElem :: (Eq m, Monoid m) => m
pattern IdentityElem <- ((== mempty) -> True) where
IdentityElem = mempty
-- -------------------------------------------------------------------- --
-- Abelianization
-- | Quotient a pair of group elements by their commutator.
--
-- The of the quotient \( G / [G,G] \) forms an abelian group, and 'Abelianizer'
-- forms a functor from the category of groups to the category of Abelian groups.
-- This functor is left adjoint to the inclusion functor \( Ab \rightarrow Grp \),
-- forming a monad in \( Grp \).
--
data Abelianizer a = Quot | Commuted a
deriving stock (Eq, Show)
instance Functor Abelianizer where
fmap _ Quot = Quot
fmap f (Commuted a) = Commuted (f a)
instance Applicative Abelianizer where
pure = Commuted
Quot <*> _ = Quot
_ <*> Quot = Quot
Commuted f <*> Commuted a = Commuted (f a)
instance Monad Abelianizer where
return = pure
(>>) = (*>)
Quot >>= _ = Quot
Commuted a >>= f = f a
instance Foldable Abelianizer where
foldMap _ Quot = mempty
foldMap f (Commuted a) = f a
instance Traversable Abelianizer where
traverse _ Quot = pure Quot
traverse f (Commuted a) = Commuted <$> f a
instance Semigroup g => Semigroup (Abelianizer g) where
Quot <> t = t
t <> Quot = t
Commuted a <> Commuted b = Commuted (a <> b)
instance Monoid g => Monoid (Abelianizer g) where
-- Normally we'd say 'Quot' but these are the same.
mempty = Commuted mempty
instance (Eq g, Group g) => Group (Abelianizer g) where
invert Quot = Quot
invert (Commuted a) = Commuted (invert a)
-- | Take the commutator of two elements of a group.
--
commutate :: Group g => g -> g -> g
commutate g g' = g <> g' <> invert g <> invert g'
{-# inline commutate #-}
-- | Quotient a pair of group elements by their commutator.
--
-- Ranging over the entire group, this operation constructs
-- the quotient of the group by its commutator sub-group
-- \( G / [G,G] \).
--
abelianize :: (Eq g, Group g) => g -> g -> Abelianizer g
abelianize g g'
| x == mempty = Quot
| otherwise = Commuted x
where
x = commutate g g'
{-# inline abelianize #-}
-- | A unidirectional pattern synonym for elements of a group
-- modulo commutators which are __not__ the identity.
--
pattern Abelianized :: (Eq g, Group g) => g -> (g,g)
pattern Abelianized x <- (uncurry abelianize -> Commuted x)
-- | A unidirectional pattern synonym for elements of a group
-- modulo commutators which are the identity.
--
pattern Quotiented :: (Eq g, Group g) => (g,g)
pattern Quotiented <- (uncurry abelianize -> Quot)
-- -------------------------------------------------------------------- --
-- Abelian (commutative) groups
{- $abelian
Commutative 'Group's.
Instances of 'Abelian' satisfy the following laws:
[Commutativity] @x <> y = y <> x@
-}