packages feed

vertexenum-0.1.0.0: src/Geometry/VertexEnum/VertexEnum.hs

module Geometry.VertexEnum.VertexEnum
  ( vertexenum
  , checkConstraints
  , interiorPoint )
  where
import           Control.Monad                   ( unless, when, (<$!>) )
import           Foreign.C.Types                 ( CDouble, CUInt )
import           Foreign.Marshal.Alloc           ( free, mallocBytes )
import           Foreign.Marshal.Array           ( peekArray, pokeArray )
import           Foreign.Storable                ( peek, sizeOf )
import           Geometry.VertexEnum.CVertexEnum ( c_intersections )
import           Geometry.VertexEnum.Constraint  ( Constraint )
import           Geometry.VertexEnum.Internal    ( iPoint, normalizeConstraints )

hsintersections :: [[Double]]     -- halfspaces
                 -> [Double]       -- interior point
                 -> Bool           -- print to stdout
                 -> IO [[Double]]
hsintersections halfspaces ipoint stdout = do
  let n     = length halfspaces
      dim   = length ipoint
  unless (all ((== dim+1) . length) halfspaces) $
    error "the points must have the same dimension"
  when (dim < 2) $
    error "dimension must be at least 2"
  when (n <= dim) $
    error "insufficient number of halfspaces"
  hsPtr <- mallocBytes (n * (dim+1) * sizeOf (undefined :: CDouble))
  pokeArray hsPtr (concatMap (map realToFrac) halfspaces)
  ipointPtr <- mallocBytes (dim * sizeOf (undefined :: CDouble))
  pokeArray ipointPtr (map realToFrac ipoint)
  exitcodePtr <- mallocBytes (sizeOf (undefined :: CUInt))
  nintersectionsPtr <- mallocBytes (sizeOf (undefined :: CUInt))
  resultPtr <- c_intersections hsPtr ipointPtr
               (fromIntegral dim) (fromIntegral n)
               nintersectionsPtr exitcodePtr (fromIntegral $ fromEnum stdout)
  exitcode <- peek exitcodePtr
  free exitcodePtr
  free hsPtr
  if exitcode /= 0
    then do
      free resultPtr
      free nintersectionsPtr
      error $ "qhull returned an error (code " ++ show exitcode ++ ")"
    else do
      nintersections <- (<$!>) fromIntegral (peek nintersectionsPtr)
      result <- (<$!>) (map (map realToFrac))
                       ((=<<) (mapM (peekArray dim))
                              (peekArray nintersections resultPtr))
      free resultPtr
      free nintersectionsPtr
      return result

-- | Vertex enumeration
vertexenum :: [Constraint]   -- ^ list of inequalities
           -> Maybe [Double] -- ^ point in the interior of the polytope
           -> IO [[Double]]
vertexenum constraints point = do
  let halfspacesMatrix = normalizeConstraints constraints
      ipoint = case point of 
        Just x  -> x
        Nothing -> iPoint halfspacesMatrix
  hsintersections halfspacesMatrix ipoint False

-- | Check whether a point fulfills some constraints; returns the 
-- difference between the upper member and the lower member for each
-- constraint, which is positive in case if the constraint is fulfilled
checkConstraints :: [Constraint]     -- ^ list of inequalities
                 -> [Double]         -- ^ point to be tested
                 -> [(Double, Bool)] -- ^ difference and status for each constraint
checkConstraints constraints point = 
  if nvars == length point + 1
    then 
      zip differences (map (>= 0) differences)
    else 
      error "The length of the point does not match the number of variables."
  where
    halfspacesMatrix = normalizeConstraints constraints
    nvars = length (head halfspacesMatrix)
    checkRow pt row = - sum (zipWith (*) row (pt ++ [1]))
    differences = map (checkRow point) halfspacesMatrix

-- | Return a point fulfilling a list of constraints
interiorPoint :: [Constraint] -> [Double]
interiorPoint constraints = iPoint halfspacesMatrix
  where
    halfspacesMatrix = normalizeConstraints constraints