I have just uploaded to Hackage version 0.1 of the species package, a Haskell library for computing with combinatorial species. Much like David Amos’s great series of posts introducing his Haskell for Maths library, I plan to write a series of posts over the next week or two introducing the library, explaining how it works, and showing off some interesting examples.
But, first things first: if you’d like to install the library and play along, just
cabal update; cabal install species
(Man, do I ever love cabal-install! But I digress.)
So, what are combinatorial species? Intuitively, a species describes a certain combinatorial structure: given an underlying set of labels, it specifies a set of structures which can be built using those labels. For example, , the species of lists, when applied to an underlying set of labels yields the set of all linear orderings of the elements of . So in general a species can be viewed as a function which takes any set (the labels) and produces another set (of structures built out of those labels).
Actually, this isn’t quite enough to capture our intuition about what a species ought to be: we want a species to work “independently” of the underlying set; which labels we use shouldn’t matter at all when it comes to describing structures. So, additionally, we require that if is a species, any bijection between two sets of labels can be “lifted” to a bijection between sets of -structures, , in a way that respects composition of bijections. (Of course, the categorists ought to be jumping out of their seats right now: all this just amounts to saying that a species is an endofunctor on the category of sets with bijections.) Importantly, it is not too hard to see that this requirement means that for any species , the size of depends only on the size of , and not on the actual elements of .
Counting labelled structures
So, let’s see some examples already! What sorts of things might we want to compute about species?
First, we of course want to be able to count how many structures are generated by a species. As a first example, consider again the species of lists. Given an underlying set of size , how many lists are there? That’s easy: .
[brent@euclid:~]$ ghci -XNoImplicitPrelude
> :m +Math.Combinatorics.Species
> take 10 $ labelled lists
labelled takes a combinatorial species as an argument, and computes an infinite list where the entry at index is the number of labelled -structures on an underlying set of size .
(This is also a good time to mention that the species library depends on the Numeric Prelude, an alternative Haskell Prelude with a mathematically sane hierarchy of numeric types; hence we must pass ghci the -XNoImplicitPrelude flag so we don’t get lots of horrible name clashes. I’ll write some additional thoughts on the Numeric Prelude in a future post.)
Now, so far this is nothing new: Dan Piponi wrote a blog post about a Haskell DSL for counting labelled structures back in 2007, and in fact, that post was part of my inspiration for this library. Counting labelled structures works by associating exponential generating functions to species. (More on this in a future post.) But we can do more than that!
Counting unlabelled structures
For one, we can also count unlabelled structures. What’s an unlabelled structure? Intuitively, it’s a structure where you can’t tell the difference between the elements of the underlying set; formally, it’s an equivalence class of labelled structures, where two labelled structures are equivalent if one can be transformed into the other by permuting the labels.
So, how about unlabelled lists?
> take 10 $ unlabelled lists
Boring! This makes sense, though: there’s only one way to make a list out of n identical objects.
But how about something a bit less trivial?
> take 10 $ labelled partitions
> take 10 $ unlabelled partitions
> :m +Math.OEIS
> description `fmap` (lookupSequence . take 10 $ labelled partitions)
Just "Bell or exponential numbers: ways of placing n labeled balls into n indistinguishable boxes."
> description `fmap` (lookupSequence . take 10 $ unlabelled partitions)
Just "a(n) = number of partitions of n (the partition numbers)."
(I couldn’t resist sneaking in a little plug for my Math.OEIS module there too. =) So, how does this work? Well… it’s a bit more complicated! But I’ll explain it in a future post, too.
But that’s not all! Not only can we count structures, we can generate them, too:
> generate lists ([1..3] :: [Int])
> generate partitions ([1..3] :: [Int])
This is a bit magical, and of course I will… explain it in a future post. For now, I leave you with this challenge: can you figure out what the asterisks are doing there? (Hint: the curly brackets denote a cycle…)
Of course, no DSL would be complete without operations with which to build up more complicated structures from simpler ones; in my next post I’ll talk about operations on combinatorial species.
If you just can’t wait for my next post and want to read more about combinatorial species, I recommend reading the Wikipedia article, which is OK, this fantastic blog post, which is what introduced me to the wonderful world of species, or for a great dead-tree reference (whence I’m getting most of my information), check out Combinatorial Species and Tree-Like Structures by Bergeron, Labelle, and Leroux.