packages feed

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

module Geometry.VertexEnum.VertexEnum
  ( vertexenum
  , checkConstraints
  , interiorPoint )
  where
import           Control.Monad                   ( unless, when, (<$!>) )
import           Data.Maybe                      ( isJust, fromJust )
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, toRationalConstraint )
import           Geometry.VertexEnum.Internal    ( iPoint, normalizeConstraints, findSigns )

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 length of the point does not match the number of variables."
  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 :: 
    Real a 
  => [Constraint a] -- ^ linear inequalities
  -> Maybe [Double] -- ^ point satisfying the inequalities, @Nothing@ for automatic point
  -> IO [[Double]]  -- ^ vertices of the polytope defined by the inequalities
vertexenum constraints point = do
  let halfspacesMatrix = 
        map (map realToFrac) (normalizeConstraints constraints)
  if isJust point
    then do
      let check = checkConstraints constraints (fromJust point)
      when (not $ all snd check) $
        error "vertexenum: the provided point does not fulfill the inequalities."
      hsintersections halfspacesMatrix (fromJust point) False
    else do
      ipoint <- interiorPoint constraints 
      hsintersections halfspacesMatrix ipoint False

-- | Checks whether a point fulfills some inequalities; returns the 
-- difference between the upper member and the lower member for each
-- inequality, which is positive in case if the inequality is fulfilled.
checkConstraints :: 
  Real a 
  => [Constraint a]   -- ^ linear 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 "checkConstraints: the length of the point does not match the number of variables."
  where
    halfspacesMatrix = 
      map (map realToFrac) (normalizeConstraints constraints)
    nvars = length (halfspacesMatrix !! 0)
    checkRow pt row = - sum (zipWith (*) row (pt ++ [1]))
    differences = map (checkRow point) halfspacesMatrix

-- | Returns a point fulfilling a list of inequalities
interiorPoint :: 
  Real a 
  => [Constraint a] -- ^ linear inequalities
  -> IO [Double]    -- ^ point fulfilling the inequaities
interiorPoint constraints = do 
  let
    constraints' = map toRationalConstraint constraints
    halfspacesMatrix = normalizeConstraints constraints'
  signs <- findSigns halfspacesMatrix 
  when (null signs) $
    error "interiorPoint: no feasible point."
  iPoint halfspacesMatrix signs