Here's another example, I think, with the admonition that I'm a programmer, not a mathematician. Even so, I was just now researching various operations in relationship to group-like structures, and came about the following example.
Mixing colours
It relates to mixing colours using Red Green Blue (RGB) colours. These are, for example, used in HTML pages.
As in my other example here on the page, I'm going to give my example in Haskell, but I'll try to provide enough information that non-programmers should be able to get the gist of it.
We can define the entire domain (set of possible values) as a data type called RGBColor
:
data RGBColor = RGBColor { red :: Byte, green :: Byte, blue :: Byte }
This simply states that an RGBColor
value has three constituent Byte
(8-bit) values for the colours red, green, and blue. The lower the number, the darker the colour, and vice versa. Here's a single example that renders a yellow colour:
RGBColor 255 255 0
If you have two colours, you can mix them by taking half of the red
component from each, half of the green
component from each, and so on.
If we have, e.g.
λ> let x = RGBColor 100 0 128
λ> let y = RGBColor 200 255 64
we can mix these two RGBColor
values (x
and y
) with a mix
function:
λ> x `mix` y
RGBColor {red = 150, green = 128, blue = 96}
This returns a new RGBColor
value with a mix of the two input colours. The mix
function is a binary operation that takes two RGBColor
values and returns an RGBColor
value. Therefore, if I've understood just a minimum of all this, it must be at least a magma. Is it anything else? I don't think it is.
Associativity
Is the mix
operation associative? No, it isn't, because it's easy to come up with a counter-example:
λ> (RGBColor 67 108 13 `mix` RGBColor 33 114 130) `mix` RGBColor 38 104 245
RGBColor {red = 44, green = 108, blue = 158}
λ> RGBColor 67 108 13 `mix` (RGBColor 33 114 130 `mix` RGBColor 38 104 245)
RGBColor {red = 52, green = 108, blue = 100}
Depending on where you put the brackets, the result is different, where it should have been the same.
Identity
Does an identity element exist for the mix
operation? Here, we can use a combination of brute force and a counter-example to show that no identity element exists.
Let's arbitrarily pick a near-black colour to start with:
λ> let nearBlack = RGBColor 1 1 1
The reason I didn't pick absolute black (RGBColor 0 0 0
) is that, due to rounding when mixing, there are eight candidates for absolute black. As we shall see, there's only one candidate for nearBlack
:
λ> filter (\e -> e `mix` nearBlack == nearBlack) allColors
[RGBColor {red = 1, green = 1, blue = 1}]
This expression searches through allColors
for a value, e
, that behaves like the left identity for nearBlack
. Here, allColors
is an enumeration of all
16,777,216 possible RGBColor
values - yes: it takes a couple of minutes to run the above search, but nothing more than that...
The point here is that if there's a left identity for mix
, it must be RGBColor 1 1 1
, because that's the only possible candidate for nearBlack
. It holds for nearBlack
:
λ> RGBColor 1 1 1 `mix` nearBlack
RGBColor {red = 1, green = 1, blue = 1}
λ> nearBlack == it
True
The return value (implicitly called it
) is, indeed, equal to nearBlack
. Can we find a counter-example where the candidate color does not behaves like the left identity?
Yes, easily:
λ> RGBColor 1 1 1 `mix` RGBColor 3 3 3
RGBColor {red = 2, green = 2, blue = 2}
λ> RGBColor 3 3 3 == it
False
The candidate RGBColor 1 1 1
does not behave as the left identity for the colour RGBColor 3 3 3
, but it was the only candidate we had. Therefore, there's no identity element for the mix
operation.
Invertibility
Is the mix
operation invertible? No. We can demonstrate that with another combination of a counter-example and brute force. Pick these two values:
λ> let a = RGBColor 94 35 172
λ> let b = RGBColor 151 185 7
These will serve as the a
and b
in the definition of invertibility. Here, I'm using the definition from Wikipedia's article on quasigroups, because that's the only remaining type of operation that mix
can be, now that I have demonstrated that it's neither associative nor has identity.
For every a
and b
, there must exist an x
and y
such that:
a `mix` x = b,
y `mix` a = b
First, try to find x
for the above a
and b
:
λ> any (\x -> a `mix` x == b) allColors
False
The built-in any
function returns True
if there's any value in allColors
that satisfy the lambda expression in the middle. The answer is False
, so there's no need to continue. The above a
and b
serve as a counter-example, because there's no x
that satisfy left division.
Summary
Mixing of RGB colours seems to be a magma, but no other type of operation. It doesn't have associativity, an identity element, and nor is it invertible.
Appendix
For programmers, here's the full code listing:
module RGB where
import Data.Bits ((.&.))
import Data.Word (Word8)
import Text.Printf (printf, PrintfType)
type Byte = Word8
data RGBColor = RGBColor { red :: Byte, green :: Byte, blue :: Byte }
deriving (Eq, Show, Bounded)
instance Enum RGBColor where
toEnum i = RGBColor r g b
where
r = toEnum $ (i .&. 0xFF0000) `div` 0x10000
g = toEnum $ (i .&. 0xFF00) `div` 0x100
b = toEnum $ i .&. 0xFF
fromEnum x =
fromEnum (red x) * 256 * 256 + fromEnum (green x) * 256 + fromEnum (blue x)
mix :: RGBColor -> RGBColor -> RGBColor
mix x y = RGBColor newRed newGreen newBlue
where
newRed = round $ (toRational ( red x) + toRational ( red y)) / 2
newGreen = round $ (toRational (green x) + toRational (green y)) / 2
newBlue = round $ (toRational ( blue x) + toRational ( blue y)) / 2
toWebColor :: PrintfType t => RGBColor -> t
toWebColor (RGBColor r g b) = printf "#%02X%02X%02X" r g b
allColors :: [RGBColor]
allColors = [minBound .. maxBound]