Hasse diagram of all subsets of a four-element set.
import Diagrams.Backend.SVG.CmdLine
{-# LANGUAGE NoMonomorphismRestriction #-}
import Diagrams.Prelude
import Data.List
import Data.Ord (comparing)
import Data.Function (on)
import Data.Maybe (fromMaybe)
import Data.Colour.SRGB (sRGB24read)
= map sRGB24read["#000000", "#D1DBBD", "#91AA9D", "#3E606F", "#193441", "#000000"] colors
A subset is represented by the size of the parent set paired with the
list of elements in the subset. isSubset
tests whether one set is a
subset of another; subsetsBySize
lists all the subsets of a set of
size n
, grouped according to size.
data Subset = Subset Int [Int]
Subset _ elts1) `isSubset` (Subset _ elts2) = all (`elem` elts2) elts1
(
subsetsBySize :: Int -> [[Subset]]
= map (map (Subset n))
subsetsBySize n . groupBy ((==) `on` length)
. sortBy (comparing length)
. subsequences
$ [1..n]
Draw the elements of a subset, by drawing a colored square for each element present, and leaving a blank space for absent elements.
= hcat
drawElts n elts . map (\i -> if i `elem` elts
then drawElt i
else strutX 1
)$ [1..n]
= unitSquare # fc (colors !! e) # lw thin drawElt e
Draw a subset by drawing a dashed rectangle around the elements. Note that we also assign a name to the rectangle, corresponding to the elements it contains, which we use to draw connections between subsets later.
Subset n elts) = ( drawElts n elts # centerXY
drawSet (<> rect (fromIntegral n + 0.5) 1.5
# dashingG [0.2,0.2] 0
# lw thin
# named elts
)
Draw a Hasse diagram by drawing subsets grouped by size in rows, and connecting each set to its subsets in the row below. See the user manual for a more in-depth explanation of how names are used to connect subsets.
= centerX . hcat' (with & sep .~ 2) . map drawSet
hasseRow
= setsD # drawConnections # centerXY
hasseDiagram n where setsD = vcat' (with & sep .~ fromIntegral n)
. map hasseRow
. reverse
$ subsets
= subsetsBySize n
subsets = applyAll connections drawConnections
To generate all the connections, we apply connectSome
to each pair
of adjacent rows, which calls connect
on those pairs where one is a
subset of the other.
= concat $ zipWith connectSome subsets (tail subsets)
connections = [ connect s1 s2 | s1 <- subs1
connectSome subs1 subs2 <- subs2
, s2 `isSubset` s2 ] , s1
Connect two subsets by looking up the subdiagrams named with their elements, and drawing a line from the upper boundary of one to the lower boundary of the other.
Subset _ elts1) (Subset _ elts2) =
connect ($ \[b1, b2] ->
withNames [elts1, elts2] ~~ boundaryFrom b2 unit_Y) # lw thin)
beneath ((boundaryFrom b1 unitY
= pad 1.1 $ hasseDiagram 4 example
= mainWith (example :: Diagram B) main