exact-cover-0.1.0.0: src/Math/ExactCover.hs
-- |
--
-- This module provides an efficient solver for exact set cover problems
-- (<http://en.wikipedia.org/wiki/Exact_cover>) using Algorithm X as described
-- in the paper /Dancing Links/, by Donald Knuth, in
-- /Millennial Perspectives in Computer Science/, P159, 2000
-- (<https://arxiv.org/abs/cs/0011047>).
--
-- For a quick start, go straight to the 'solve' function.
module Math.ExactCover
(
-- * Mathematical definition
-- $def
-- * Simple interface
solve
-- * Types
, ExactCoverProblem
-- * Construction
, transform
-- * Solvers
, solveEC
)
where
import Math.ExactCover.Internal.DLX
import Foreign
import System.IO.Unsafe (unsafePerformIO)
import Control.Concurrent (MVar, newMVar, withMVar)
import Control.Monad (forM, forM_, when)
import Data.Map (Map)
import qualified Data.Map.Strict as Map (lookup, keys)
import Data.Set (Set)
import qualified Data.Set as Set (size, toList, fromList)
-- | Basic type that represents the exact cover problem.
data ExactCoverProblem setlabel a = ExactCoverProblem
{ ec_sets :: !(Map (Set a) setlabel) -- ^ Associates each set with a label.
, ec_dlx :: MVar (ForeignPtr DLXMatrix) -- ^ Dancing links representation.
}
-- PROGRAMMING NOTE: The Enum typeclass is needed as the set objects are
-- internally represented as integers in the C portion of the library.
-- | Constructs an 'ExactCoverProblem' given a collection of subsets \\(
-- \\mathcal{S} \\) as represented by a 'Map' between each subset and its label.
-- The set \\( \\mathcal{X} \\) over which the exact cover is to be found is
-- assumed to be the union of the given collection of subsets \\( \\mathcal{S}
-- \\).
transform :: (Enum a)
=> Map (Set a) setlabel
-> ExactCoverProblem setlabel a
transform m = unsafePerformIO $ do
dlxHead <- newForeignPtr c_free_matrix =<< c_create_empty_matrix
withForeignPtr dlxHead $ \hPtr ->
forM_ (Map.keys m) $ \k ->
withArray (fromIntegral . fromEnum <$> Set.toList k) $ \constrainPtr ->
with hPtr $ \hPPtr -> do
ret <- c_add_set hPPtr constrainPtr (fromIntegral $ Set.size k)
when (ret /= 0) $ error "Could not add constraint."
dlxM <- newMVar dlxHead
pure $ ExactCoverProblem { ec_sets = m
, ec_dlx = dlxM
}
-- | Solves the given 'ExactCoverProblem', returning the labels of the subsets
-- that form the exact cover.
solveEC :: (Enum a, Ord a)
=> ExactCoverProblem setlabel a
-> [setlabel]
solveEC ExactCoverProblem{ ec_sets = setLabels, ec_dlx = dlxM } = unsafePerformIO $
withMVar dlxM $ \dlxHead ->
withForeignPtr dlxHead $ \hPtr ->
alloca $ \setCoversPtrPtrPtr ->
alloca $ \setCoverSizesPtrPtr ->
alloca $ \nSetsPtr -> do
ret <- c_solve hPtr 4 setCoversPtrPtrPtr setCoverSizesPtrPtr nSetsPtr
case () of
_ | ret < 0 -> error ""
| ret == 0 -> do
-- Retrieve number of result sets.
nSets <- fromIntegral <$> peek nSetsPtr
-- Retrieve list of set sizes.
setCoverSizesPtr <- peek setCoverSizesPtrPtr
setCoverSizes <- pure . map fromIntegral
=<< peekArray nSets setCoverSizesPtr
free setCoverSizesPtr
-- Retrieve list of pointers to the result sets.
setCoversPtrPtr <- peek setCoversPtrPtrPtr
setCoversPtr <- peekArray nSets setCoversPtrPtr
free setCoversPtrPtr
-- Retrieve the result sets.
forM (zip setCoverSizes setCoversPtr) $ \(setSize, setPtr) -> do
setC <- Set.fromList . map (toEnum . fromIntegral)
<$> peekArray setSize setPtr
free setPtr
pure $ case Map.lookup setC setLabels of
Nothing -> error "Constrain set not in map."
Just label -> label
| ret == 1 -> pure mempty
| otherwise -> error "Unknown error code."
-- | Given a collection of subsets \\( \\mathcal{S} \\), represented by a 'Map'
-- between each subset (of type @'Set' a@) and its label, returns a list of
-- labels that represents the exact cover \\( \\mathcal{S}^{*} \\).
--
-- Example: To find the exact cover of the collection of subsets
-- \\( \\left\\{\\left\\{2,4,5\\right\\}, \\left\\{0,3,6\\right\\},
-- \\left\\{1,2,5\\right\\}, \\left\\{0,3\\right\\}, \\left\\{1,6\\right\\},
-- \\left\\{3,4,6\\right\\}\\right\\} \\),
--
-- > solve (Map.fromList [ (Set.fromList [2,4,5], 'A')
-- > , (Set.fromList [0,3,6], 'B')
-- > , (Set.fromList [1,2,5], 'C')
-- > , (Set.fromList [0,3], 'D')
-- > , (Set.fromList [1,6], 'E')
-- > , (Set.fromList [3,4,6], 'F')
-- > ] :: Map (Set Int) Char)
-- > == "DAE"
solve :: (Enum a, Ord a) => Map (Set a) setlabel -> [setlabel]
solve = solveEC . transform
-- $def
--
-- Given a collection \\( \\mathcal{S} \\) of subsets of a set \\( \\mathcal{X}
-- \\), an exact cover is a subcollection \\( \\mathcal{S}^{*} \\) of \\(
-- \\mathcal{S} \\) such that each element in \\( \\mathcal{X} \\) is contained
-- in exactly one subset in \\( \\mathcal{S}^{*} \\) (from wikipedia).