In [1]:
:e TypeFamilies
:e GeneralizedNewtypeDeriving
:e DeriveTraversable
:e TypeSynonymInstances
:e FlexibleInstances
:e ApplicativeDo
:e LambdaCase
:e TupleSections

import Graphics.Gloss.Interface.IO.Simulate (simulateIO, Display (FullScreen))
import Graphics.Gloss (white, Picture (Text, Polygon, Color, Circle, Pictures), simulate, Rectangle (Rectangle), black, red, translate)
import Control.Monad.Bayes.Class (MonadDistribution(bernoulli, uniformD, random, normal), factor, MonadMeasure)
import Control.Monad.Bayes.Sampler.Strict (sampler, sampleIOfixed)
import Control.Monad.Bayes.Traced (mh, mhStep)
import Control.Monad.Bayes.Weighted
import Data.Functor.Compose (Compose(..))
import qualified Data.Vector as V
import Data.Bool (bool)
import Data.Distributive (Distributive(..))
import Data.Functor.Rep (Representable(..), distributeRep, ifoldMapRep)
import Data.Functor.Identity (Identity(..))
import Control.Arrow ((***), Arrow (first, second))
import Control.Comonad.Representable.Store (Store(..), StoreT(..), store, experiment, runStore)
import Control.Comonad (Comonad(..), (=>>))
import Graphics.Gloss.Data.Color (Color)
import Control.Monad (filterM)
import Debug.Trace (trace)
import Data.Monoid (Sum(Sum, getSum))
import Control.Monad.Bayes.Inference.SMC 
import Control.Monad.Bayes.Population 
import qualified Pipes.Prelude as P
import Pipes ((>->))
import Graphics.Gloss.Interface.IO.Animate (animateIO)
import Data.IORef (newIORef, writeIORef)
import GHC.IORef (readIORef)
import qualified Pipes as P
import Control.Concurrent (forkIO, threadDelay)
import Control.Monad.IO.Class
import Control.Monad.Bayes.Sampler.Strict

The Ising model is a famous model in statistical mechanics. See the wonderful https://jaan.io/how-does-physics-connect-machine-learning/. Basically, it's a big grid of squares, each of which can be in one of two states. We can assign an "interaction energy" between adjacent states, which says how much they like to be in similar or different states. A configuration of the whole grid (i.e. choosing a setting for each square) determines a global energy, and in turn, a distribution (the Boltzmann distribution) over grids.

The code borrows heavily from the excellent https://chrispenner.ca/posts/conways-game-of-life which uses a representable store comonad to perform efficient local updates to a grid. The approach should extend easily to similar models with different topologies.

In [2]:
------
-- Define a board as a length 20 array of length 20 arrays
-- Define a grid as a board and a pointer to a square
------

-- | Grid a ~ ([[a]], (Int, Int))
type Grid a = Store (Compose VBounded VBounded) a

type Table = Compose VBounded VBounded

-- | VBounded a ~ [a]
newtype VBounded a = VBounded (V.Vector a)
  deriving (Eq, Show, Functor, Foldable, Traversable)

instance Distributive VBounded where
  distribute = distributeRep

-- | size of the Ising grid
gridSize :: Int
gridSize = 40

-- | you can view a grid as a function from indices to square values
-- or as an array of square values.
-- These two structures are isomorphic, as witnessed by: 
-- index . tabulate === id === tabulate . index
instance Representable VBounded where
  type Rep VBounded = Int
  index (VBounded v) i = v V.! (i `mod` gridSize)
  tabulate desc = VBounded $ V.generate gridSize desc

-- | make a grid
mkGrid :: [(Int, Int)] -> Grid Bool
mkGrid xs = store (`elem` xs) (0, 0)

Approach 1: calculate the energy and perform Markov Chain Monte Carlo¶

There are two ways we can simulate an Ising model. The first is to define a distribution over grids directly, by drawing from a uniform prior, and adding a factor statement for the energy. One can then sample from this distribution using MCMC, and indeed, the MCMC chain will look like a time-evolving Ising model. Which is very cool!

Let's do that first:

In [3]:
-- | calculate the energy of a particular square in the grid 
-- (which depends on the rest of the grid, in particular its neighbours)
interactionEnergy :: Grid Bool -> Int
interactionEnergy g = numNeighboursSame
  where
    numNeighboursSame = length (filter (==color) neighbours)
    color = extract g
    neighbours = experiment (\s -> addCoords s <$> neighbourCoords) g
    addCoords (x, y) (x', y') = (x + x', y + y')
    neighbourCoords = [(x, y) | x <- [-1, 0, 1], y <- [-1, 0, 1], (x, y) /= (0, 0)]
In [4]:
-- | The prior is uniform over all configurations of the grid
-- We score states based on their energy
model :: MonadMeasure m => m (Grid Bool)
model = do
    let allCoords = [(x,y) | x <- [0..gridSize-1], y <- [0..gridSize-1]]
    coords <- filterM (const $ bernoulli 0.5) allCoords
    let grid = mkGrid coords
        StoreT (Identity energy) _ = extend interactionEnergy grid
        score  = getSum $ foldMap Sum energy
    factor $ exp $ fromIntegral score
    return grid
In [5]:
---
-- render a grid into ascii using an indexed fold
---

display :: Grid Bool -> String
display (StoreT (Identity grid) _) = ifoldMapRep match grid where
    match (_,n) b 
        | n==0 = "\n|" <> showB b
        | n==gridSize-1 = "|" <> showB b <> "|"
        | otherwise = "|" <> showB b
    showB True = "X"
    showB False = "O"
    
samples = do
  s <- sampler $ unweighted $ mh 2 model
  mapM_ (\x -> putStrLn ("\n\n" <> display x)) $ reverse $ take 2 s
In [6]:
samples


|O|X|O|O|X|X|X|O|X|O|O|O|X|O|O|X|X|X|X|O|X|X|O|O|O|X|O|O|O|X|X|O|O|O|X|O|O|O|X|O|
|O|O|X|X|O|O|O|O|X|O|O|X|X|X|X|X|X|O|X|O|O|X|X|X|X|X|X|O|X|X|X|X|O|O|X|X|O|O|X|X|
|O|O|O|X|X|X|O|O|O|X|O|X|O|X|O|O|O|X|X|X|O|O|O|X|O|O|O|X|X|O|X|O|O|X|O|O|O|O|O|X|
|O|O|X|O|O|X|O|O|O|O|X|X|O|O|X|O|O|O|X|O|X|O|O|X|O|X|X|O|X|X|O|O|X|X|X|X|O|O|O|O|
|X|X|X|O|O|X|O|O|X|O|X|X|X|O|X|O|O|X|O|O|X|X|O|O|X|O|O|O|X|O|X|X|O|O|O|O|O|O|X|O|
|O|O|X|X|O|O|X|X|X|O|O|X|O|X|X|O|O|O|X|O|X|O|O|X|X|X|O|X|X|O|O|O|O|O|X|O|O|O|X|O|
|O|O|O|O|O|X|O|O|O|O|X|X|O|X|O|O|X|X|X|O|O|O|O|O|X|X|O|O|X|O|O|O|X|X|O|X|O|O|X|X|
|X|O|O|O|X|O|O|X|O|O|O|O|X|O|O|X|O|X|X|X|X|O|O|X|O|X|O|O|X|X|O|O|O|X|O|X|X|X|X|X|
|O|O|X|O|X|O|X|X|O|X|X|X|O|X|X|X|O|X|O|O|O|O|X|O|X|O|O|X|X|X|X|O|O|O|O|X|O|X|X|X|
|O|O|X|X|X|O|O|X|O|O|X|X|O|O|O|X|O|X|X|X|X|O|X|X|X|X|X|X|O|X|X|X|X|X|O|X|O|O|X|X|
|X|X|X|O|X|X|O|X|O|O|X|X|X|O|X|O|O|O|O|O|O|O|X|O|O|O|X|X|X|X|X|X|O|O|O|X|X|X|X|X|
|O|X|O|O|X|O|X|X|X|X|O|X|X|X|X|X|X|X|O|O|O|O|X|O|O|X|X|O|O|O|X|X|X|O|X|O|X|O|O|O|
|X|O|X|O|X|O|O|X|X|X|O|O|X|O|O|O|O|X|O|O|X|O|O|O|X|X|O|X|X|X|X|O|X|O|X|O|X|X|O|O|
|X|O|X|O|O|O|O|X|O|X|O|O|O|X|X|O|O|X|O|O|O|X|O|X|X|X|X|O|X|O|O|O|O|O|X|O|O|O|X|X|
|X|O|O|O|O|X|X|O|O|O|X|X|X|O|X|X|O|X|O|X|X|X|O|X|X|X|O|O|X|O|X|X|X|O|X|X|X|X|O|X|
|X|O|O|O|O|O|X|O|O|X|X|O|O|O|O|X|X|O|X|X|O|X|X|X|X|X|X|X|O|O|O|X|X|X|O|O|O|O|O|X|
|X|O|O|X|O|O|X|X|O|X|X|X|X|O|X|X|X|O|O|X|X|O|O|X|O|O|X|X|X|X|O|X|O|X|O|O|O|X|X|O|
|X|X|X|X|O|X|O|O|O|X|O|X|O|O|X|O|O|O|X|X|X|O|X|X|O|O|O|X|X|X|X|O|X|X|O|O|O|X|O|O|
|O|O|O|X|O|O|O|X|X|X|O|X|O|X|O|O|X|X|X|X|O|X|X|O|X|X|X|O|X|X|X|X|X|X|X|O|O|X|O|X|
|X|O|X|O|X|X|O|O|O|X|X|X|O|O|O|O|O|O|O|X|O|X|O|X|X|X|O|X|X|O|O|X|X|X|O|X|O|O|X|O|
|O|X|O|X|X|O|X|X|X|X|X|X|O|O|X|O|O|X|X|X|O|X|X|X|O|O|X|O|X|O|X|O|O|O|O|O|X|O|X|O|
|O|X|X|X|O|X|O|X|X|O|X|O|O|O|O|X|X|X|O|X|X|O|X|O|O|X|O|X|O|X|O|O|O|O|O|X|O|O|O|X|
|O|X|X|X|O|X|X|X|O|O|X|O|O|X|O|X|X|X|X|X|O|O|X|O|X|O|O|O|X|X|O|O|X|X|O|O|X|O|X|X|
|X|X|O|O|O|O|X|X|X|O|O|X|X|X|O|O|X|O|X|O|X|X|X|O|X|O|X|O|X|O|O|O|O|O|X|X|O|O|X|O|
|O|O|O|X|X|X|X|O|O|O|O|O|O|O|X|O|X|O|X|O|O|X|X|X|X|O|X|O|X|X|X|X|O|X|X|O|O|X|O|X|
|O|X|X|O|O|X|O|O|X|X|O|X|O|O|O|O|O|O|X|O|X|X|X|X|X|O|O|X|X|X|X|X|X|O|X|X|X|X|O|O|
|X|O|O|O|X|X|X|O|X|X|O|O|X|X|X|O|X|O|X|O|X|X|X|X|O|O|O|O|O|O|O|X|X|O|X|O|X|O|O|O|
|X|O|X|O|O|O|X|X|X|O|O|X|X|O|X|O|O|O|X|X|X|O|O|O|O|O|O|O|O|O|O|X|O|X|O|O|O|O|X|X|
|O|X|O|X|O|X|X|O|X|X|O|O|O|X|O|X|O|X|O|O|O|O|X|O|O|O|O|X|X|X|O|O|O|X|X|O|X|O|X|X|
|O|O|O|O|O|X|X|O|X|X|X|O|X|O|X|X|O|O|O|O|O|X|O|O|O|X|O|O|O|X|X|X|X|O|X|X|X|X|X|X|
|X|O|X|O|O|X|O|O|O|X|X|O|X|O|O|O|O|O|O|X|O|X|X|X|O|O|O|X|O|O|X|O|X|X|O|X|X|X|O|X|
|X|O|O|O|O|X|O|X|O|O|O|O|X|X|O|O|X|X|O|X|X|X|X|O|X|X|O|O|O|X|X|O|O|O|X|O|O|O|O|O|
|X|X|X|O|O|X|O|X|O|X|X|X|O|O|O|X|X|O|X|X|O|O|X|O|O|X|O|X|X|X|O|O|O|X|O|O|O|O|X|X|
|X|X|X|X|X|X|O|X|O|O|O|O|X|X|O|O|X|X|X|X|O|O|X|O|O|X|X|O|X|O|X|O|X|X|O|O|O|O|O|O|
|O|X|O|O|X|X|X|X|X|X|O|O|X|X|O|X|O|X|X|X|X|O|X|X|O|O|X|O|O|X|O|O|O|O|X|X|X|X|O|O|
|O|O|O|X|O|O|X|O|O|X|X|X|X|X|X|X|X|X|O|O|O|X|X|O|O|O|X|O|X|O|O|X|O|X|O|X|O|X|O|X|
|X|X|O|O|X|X|X|O|X|O|X|O|O|X|X|O|O|X|X|O|O|O|O|O|X|X|X|O|O|X|X|X|O|X|O|X|O|X|X|X|
|O|X|O|O|O|X|X|X|X|O|X|X|X|O|O|X|O|X|X|O|X|X|O|O|X|O|X|X|O|O|X|X|O|O|O|O|O|X|O|O|
|X|O|X|X|X|O|O|X|X|X|X|O|O|X|O|O|X|X|O|O|O|O|X|O|X|X|O|O|X|O|X|O|O|O|X|X|O|X|O|X|
|O|X|X|O|X|O|O|X|X|O|O|O|O|O|X|X|O|O|O|X|O|X|O|O|O|X|X|O|X|O|X|X|O|X|X|O|O|O|X|X|



|O|X|O|O|X|X|X|O|X|O|O|O|X|O|O|X|X|X|X|O|X|X|O|O|O|X|O|O|O|X|X|O|O|O|X|O|O|O|X|O|
|O|O|X|X|O|O|O|O|X|O|O|X|X|X|X|X|X|O|X|O|O|X|X|X|X|X|X|O|X|X|X|X|O|O|X|X|O|O|X|X|
|O|O|O|X|X|X|O|O|O|X|O|X|O|X|O|O|O|X|X|X|O|O|O|X|O|O|O|X|X|O|X|O|O|X|O|O|O|O|O|X|
|O|O|X|O|O|X|O|O|O|O|X|X|O|O|X|O|O|O|X|O|X|O|O|X|O|X|X|O|X|X|O|O|X|X|X|X|O|O|O|O|
|X|X|X|O|O|X|O|O|X|O|X|X|X|O|X|O|O|X|O|O|X|X|O|O|X|O|O|O|X|O|X|X|O|O|O|O|O|O|X|O|
|O|O|X|X|O|O|X|X|X|O|O|X|O|X|X|O|O|O|X|O|X|O|O|X|X|X|O|X|X|O|O|O|O|O|X|O|O|O|X|O|
|O|O|O|O|O|X|O|O|O|O|X|X|O|X|O|O|X|X|X|O|O|O|O|O|X|X|O|O|X|O|O|O|X|X|O|X|O|O|X|X|
|X|O|O|O|X|O|O|X|O|O|O|O|X|O|O|X|O|X|X|X|X|O|O|X|O|X|O|O|X|X|O|O|O|X|O|X|X|X|X|X|
|O|O|X|O|X|O|X|X|O|X|X|X|O|X|X|X|O|X|O|O|O|O|X|O|X|O|O|X|X|X|X|O|O|O|O|X|O|X|X|X|
|O|O|X|X|X|O|O|X|O|O|X|X|O|O|O|X|O|X|X|X|X|O|X|X|X|X|X|X|O|X|X|X|X|X|O|X|O|O|X|X|
|X|X|X|O|X|X|O|X|O|O|X|X|X|O|X|O|O|O|O|O|O|O|X|O|O|O|X|X|X|X|X|X|O|O|O|X|X|X|X|X|
|O|X|O|O|X|O|X|X|X|X|O|X|X|X|X|X|X|X|O|O|O|O|X|O|O|X|X|O|O|O|X|X|X|O|X|O|X|O|O|O|
|X|O|X|O|X|O|O|X|X|X|O|O|X|O|O|O|O|X|O|O|X|O|O|O|X|X|O|X|X|X|X|O|X|O|X|O|X|X|O|O|
|X|O|X|O|O|O|O|X|O|X|O|O|O|X|X|O|O|X|O|O|O|X|O|X|X|X|X|O|X|O|O|O|O|O|X|O|O|O|X|X|
|X|O|O|O|O|X|X|O|O|O|X|X|X|O|X|X|O|X|O|X|X|X|O|X|X|X|O|O|X|O|X|X|X|O|X|X|X|X|O|X|
|X|O|O|O|O|O|X|O|O|X|X|O|O|O|O|X|X|O|X|X|O|X|X|X|X|X|X|X|O|O|O|X|X|X|O|O|O|O|O|X|
|X|O|O|X|O|O|X|X|O|X|X|X|X|O|X|X|X|O|O|X|X|O|O|X|O|O|X|X|X|X|O|X|O|X|O|O|O|X|X|O|
|X|X|X|X|O|X|O|O|O|X|O|X|O|O|X|O|O|O|X|X|X|O|X|X|O|O|O|X|X|X|X|O|X|X|O|O|O|X|O|O|
|O|O|O|X|O|O|O|X|X|X|O|X|O|X|O|O|X|X|X|X|O|X|X|O|X|X|X|O|X|X|X|X|X|X|X|O|O|X|O|X|
|X|O|X|O|X|X|O|O|O|X|X|X|O|O|O|O|O|O|O|X|O|X|O|X|X|X|O|X|X|O|O|X|X|X|O|X|O|O|X|O|
|O|X|O|X|X|O|X|X|X|X|X|X|O|O|X|O|O|X|X|X|O|X|X|X|O|O|X|O|X|O|X|O|O|O|O|O|X|O|X|O|
|O|X|X|X|O|X|O|X|X|O|X|O|O|O|O|X|X|X|O|X|X|O|X|O|O|X|O|X|O|X|O|O|O|O|O|X|O|O|O|X|
|O|X|X|X|O|X|X|X|O|O|X|O|O|X|O|X|X|X|X|X|O|O|X|O|X|O|O|O|X|X|O|O|X|X|O|O|X|O|X|X|
|X|X|O|O|O|O|X|X|X|O|O|X|X|X|O|O|X|O|X|O|X|X|X|O|X|O|X|O|X|O|O|O|O|O|X|X|O|O|X|O|
|O|O|O|X|X|X|X|O|O|O|O|O|O|O|X|O|X|O|X|O|O|X|X|X|X|O|X|O|X|X|X|X|O|X|X|O|O|X|O|X|
|O|X|X|O|O|X|O|O|X|X|O|X|O|O|O|O|O|O|X|O|X|X|X|X|X|O|O|X|X|X|X|X|X|O|X|X|X|X|O|O|
|X|O|O|O|X|X|X|O|X|X|O|O|X|X|X|O|X|O|X|O|X|X|X|X|O|O|O|O|O|O|O|X|X|O|X|O|X|O|O|O|
|X|O|X|O|O|O|X|X|X|O|O|X|X|O|X|O|O|O|X|X|X|O|O|O|O|O|O|O|O|O|O|X|O|X|O|O|O|O|X|X|
|O|X|O|X|O|X|X|O|X|X|O|O|O|X|O|X|O|X|O|O|O|O|X|O|O|O|O|X|X|X|O|O|O|X|X|O|X|O|X|X|
|O|O|O|O|O|X|X|O|X|X|X|O|X|O|X|X|O|O|O|O|O|X|O|O|O|X|O|O|O|X|X|X|X|O|X|X|X|X|X|X|
|X|O|X|O|O|X|O|O|O|X|X|O|X|O|O|O|O|O|O|X|O|X|X|X|O|O|O|X|O|O|X|O|X|X|O|X|X|X|O|X|
|X|O|O|O|O|X|O|X|O|O|O|O|X|X|O|O|X|X|O|X|X|X|X|O|X|X|O|O|O|X|X|O|O|O|X|O|O|O|O|O|
|X|X|X|O|O|X|O|X|O|X|X|X|O|O|O|X|X|O|X|X|O|O|X|O|O|X|O|X|X|X|O|O|O|X|O|O|O|O|X|X|
|X|X|X|X|X|X|O|X|O|O|O|O|X|X|O|O|X|X|X|X|O|O|X|O|O|X|X|O|X|O|X|O|X|X|O|O|O|O|O|O|
|O|X|O|O|X|X|X|X|X|X|O|O|X|X|O|X|O|X|X|X|X|O|X|X|O|O|X|O|O|X|O|O|O|O|X|X|X|X|O|O|
|O|O|O|X|O|O|X|O|O|X|X|X|X|X|X|X|X|X|O|O|O|X|X|O|O|O|X|O|X|O|O|X|O|X|O|X|O|X|O|X|
|X|X|O|O|X|X|X|O|X|O|X|O|O|X|X|O|O|X|X|O|O|O|O|O|X|X|X|O|O|X|X|X|O|X|O|X|O|X|X|X|
|O|X|O|O|O|X|X|X|X|O|X|X|X|O|O|X|O|X|X|O|X|X|O|O|X|O|X|X|O|O|X|X|O|O|O|O|O|X|O|O|
|X|O|X|X|X|O|O|X|X|X|X|O|O|X|O|O|X|X|O|O|O|O|X|O|X|X|O|O|X|O|X|O|O|O|X|X|O|X|O|X|
|O|X|X|O|X|O|O|X|X|O|O|O|O|O|X|X|O|O|O|X|O|X|O|O|O|X|X|O|X|O|X|X|O|X|X|O|O|O|X|X|

Approach 2: perform the stochastic evolution directly¶

Instead of indirectly obtaining the Ising model time evolution as a Markov Chain over a model, we can also just directly write the kernel which moves from one state of a grid to the next, stochastically. We express the chain as a stream (a Producer) and display that.

The one subtlety is that we need a function Store (m a) -> m (Store a), i.e. a Traversable instance for Store, so that we can expand local stochasticity to global stochasticity.

In [7]:
instance Foldable (Store g)
instance (Traversable g, Representable g, Functor g) => Traversable (Store g) where
  sequenceA g = fmap (uncurry store . first index) (distributed g) where
    distributed :: (Representable g, Traversable g, Applicative m) => Store g (m a) -> m (g a, Rep g)
    distributed  g = grab $
      first (sequenceA . tabulate) $
      runStore g
    grab (ma, b) = (,b) <$> ma

step :: MonadDistribution m =>  Grid Bool -> m (Grid Bool)
step = sequence . extend rule 

rule :: MonadDistribution m => Grid Bool -> m Bool
rule = experiment (uniformD . neighbours)
  where
    neighbours s = addCoords s <$> neighbourCoords
    addCoords (x, y) (x', y') = (x + x', y + y')
    neighbourCoords = [(x, y) | x <- [-1, 0, 1], y <- [-1, 0, 1], (x, y) /= (0, 0)]
In [9]:
steps :: MonadDistribution m => P.Producer (Grid Bool) m ()
steps = P.unfoldr
          (fmap (Right . (\y -> (y,y))) . step)
          (mkGrid [(m,n) | m <- [0..10], n <- [0..20]])
          

sampler $ P.runEffect (steps >-> P.drop 50 >-> P.mapM (liftIO . putStrLn . display) >-> P.take 2 >-> P.drain)
|O|O|O|O|O|O|X|X|O|X|X|X|O|O|X|O|X|O|O|O|O|O|O|O|O|O|X|O|X|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|X|O|X|O|X|X|O|X|X|O|O|O|O|O|O|O|X|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|X|O|X|X|O|X|X|O|X|X|O|O|O|X|O|O|X|O|X|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|X|O|O|X|O|X|X|O|O|X|O|O|O|O|X|O|X|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|X|O|X|X|X|O|X|O|O|O|O|X|O|O|O|O|O|O|X|X|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|X|O|X|O|X|O|O|O|X|X|O|X|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|X|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|X|X|O|X|X|O|O|O|O|X|X|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|X|O|X|O|O|O|X|O|X|O|O|O|X|O|O|O|O|O|O|O|O|O|O|X|X|
|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|
|O|O|O|O|O|O|O|O|X|X|O|O|O|O|O|O|X|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|
|X|O|O|O|X|O|O|X|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|
|X|X|O|O|X|O|O|O|O|X|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|
|O|O|X|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|X|O|X|X|
|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|X|O|O|O|X|X|X|X|
|O|O|O|O|O|O|X|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|X|X|O|O|O|O|O|X|O|O|O|
|X|O|O|X|X|X|O|X|O|O|X|O|O|X|O|O|X|O|X|X|O|O|O|O|O|O|O|O|O|O|O|X|O|O|X|O|O|O|X|X|
|X|O|X|O|X|O|O|X|O|X|O|O|O|O|O|O|O|X|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|
|O|O|O|O|O|O|O|O|X|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|X|O|O|O|O|X|
|X|O|O|O|X|X|O|X|X|O|O|O|O|O|O|X|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|
|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|X|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|X|O|X|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|X|X|O|O|O|X|O|O|O|O|X|O|O|O|O|O|O|X|O|O|O|O|O|O|
|O|O|O|X|O|O|O|X|O|O|O|O|O|O|O|O|O|X|X|O|O|O|O|O|O|O|O|O|X|O|O|O|O|X|X|O|X|O|O|O|
|O|O|O|O|O|O|O|O|O|O|X|O|X|O|O|O|X|X|X|X|O|X|O|O|X|O|O|O|O|O|X|O|X|O|X|O|O|O|O|O|
|O|O|O|O|O|O|O|O|X|O|X|X|X|O|O|X|O|X|X|X|O|X|O|O|O|O|O|X|O|O|X|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|X|X|O|X|X|O|X|X|O|O|O|X|O|O|O|O|X|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|

|O|O|O|O|O|O|O|O|O|X|X|X|X|O|X|O|O|O|O|O|O|O|O|O|X|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|X|O|X|O|O|X|O|X|O|O|O|O|O|O|O|O|X|O|X|O|O|X|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|X|O|X|X|X|O|X|O|O|O|O|O|O|O|X|X|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|X|X|O|X|X|X|X|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|X|O|O|X|O|X|O|X|X|X|X|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|X|O|O|O|O|O|X|O|X|O|O|X|O|O|O|O|X|O|O|X|O|X|O|O|O|X|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|X|X|O|O|X|O|O|X|O|O|X|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|X|X|O|O|O|X|X|O|O|O|O|X|X|O|O|O|O|O|O|O|O|X|O|
|X|O|O|O|O|O|O|O|O|X|O|O|X|O|O|O|X|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|
|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|X|O|O|X|O|O|O|X|X|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|X|X|X|
|O|O|O|X|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|X|X|O|
|X|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|X|
|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|X|X|O|O|O|X|
|O|X|O|O|O|O|X|O|X|O|O|X|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|X|X|O|O|O|O|O|X|X|X|X|X|
|O|O|X|X|O|O|X|X|X|O|O|O|O|O|O|O|O|O|X|O|X|O|O|O|O|O|O|O|O|X|O|O|O|X|O|O|X|X|X|O|
|O|O|O|O|X|O|X|O|O|O|O|O|O|X|X|O|O|O|X|X|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|
|O|X|O|X|O|X|X|O|X|O|O|O|O|O|X|O|O|O|X|O|X|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|X|O|X|
|X|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|X|O|O|X|O|
|X|X|X|O|O|X|X|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|
|O|X|O|O|O|O|X|X|X|O|O|O|O|O|O|O|X|O|O|O|X|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|O|
|O|O|O|O|X|O|X|O|O|O|O|O|O|O|O|O|O|O|X|X|O|O|O|O|O|O|X|O|O|O|O|O|O|O|O|X|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|O|X|X|O|O|X|X|O|X|O|O|O|X|O|O|O|O|O|O|O|O|O|X|X|O|O|O|X|O|O|
|O|O|X|O|O|O|O|O|O|X|O|X|X|O|O|X|X|O|X|O|O|O|O|X|O|O|X|O|O|X|X|O|O|X|O|O|O|O|O|O|
|O|O|O|O|O|O|O|O|O|O|X|X|X|X|X|O|X|O|X|X|O|X|X|O|O|O|O|O|O|O|O|O|O|O|X|O|O|O|O|O|
|O|O|O|O|O|O|O|X|O|O|X|O|X|X|O|O|O|O|O|X|X|O|O|O|O|X|X|O|O|O|O|O|O|O|O|O|O|O|O|O|
In [11]:
import IHaskell.Display.Widgets.Interactive
import IHaskell.Display.Widgets

mkPlay
<interactive>:1:1: error:
    Could not find module ‘IHaskell.Display.Widgets.Interactive’
    Use -v (or `:set -v` in ghci) to see a list of the files searched for.
In [ ]: