packages feed

haskell-igraph-0.8.5: src/IGraph/Algorithms/Layout.chs

{-# LANGUAGE ForeignFunctionInterface #-}
module IGraph.Algorithms.Layout
    ( layout
    , LayoutMethod(..)
    , kamadaKawai
    , lgl
    ) where

import           Data.Maybe             (isJust, fromMaybe)
import           Foreign                (nullPtr)
import           System.IO.Unsafe          (unsafePerformIO)
import IGraph

import Foreign
import IGraph.Random
{#import IGraph.Internal #}

#include "haskell_igraph.h"

layout :: Graph d v e -> LayoutMethod -> Gen -> [(Double, Double)]
layout gr method _ = unsafePerformIO $ case method of
    Random -> allocaMatrix $ \mat -> do
        igraphLayoutRandom gptr mat
        getResult mat

    KamadaKawai seed niter kkconst epsilon -> do
        let f mat = igraphLayoutKamadaKawai gptr mat (isJust seed) niter
                epsilon (fromMaybe (fromIntegral $ nNodes gr) kkconst) nullPtr
                nullPtr nullPtr nullPtr nullPtr
        case seed of
            Nothing -> allocaMatrix $ \mat -> do
                f mat
                getResult mat
            Just s -> withRowLists ((\(x,y) -> [x,y]) $ unzip s) $ \mat -> do
                f mat
                getResult mat

    LGL niter delta area coolexp repulserad cellsize -> allocaMatrix $ \mat -> do
        igraphLayoutLgl gptr mat niter (delta n) (area n) coolexp
            (repulserad n) (cellsize n) (-1)
        getResult mat
  where
    n = nNodes gr
    gptr = _graph gr
    getResult mat = (\[x, y] -> zip x y) <$> toColumnLists mat

data LayoutMethod =
    Random 
  | KamadaKawai { kk_seed      :: Maybe [(Double, Double)]
                , kk_nIter     :: Int
                , kk_const     :: Maybe Double  -- ^ The Kamada-Kawai vertex attraction constant
                , kk_epsilon   :: Double
                }   -- ^ The Kamada-Kawai algorithm. Time complexity: O(|V|)
                    -- for each iteration, after an O(|V|^2 log|V|)
                    -- initialization step. 
  | LGL { lgl_nIter      :: !Int
        , lgl_maxdelta   :: (Int -> Double)  -- ^ The maximum length of the move allowed
        -- for a vertex in a single iteration. A reasonable default is the number of vertices.
        , lgl_area       :: (Int -> Double)  -- ^ This parameter gives the area
        -- of the square on which the vertices will be placed. A reasonable
        -- default value is the number of vertices squared.
        , lgl_coolexp    :: !Double  -- ^ The cooling exponent. A reasonable default value is 1.5.
        , lgl_repulserad :: (Int -> Double) -- ^ Determines the radius at which
        -- vertex-vertex repulsion cancels out attraction of adjacent vertices.
        -- A reasonable default value is area times the number of vertices.
        , lgl_cellsize   :: (Int -> Double)
        }

-- | Default parameters for the Kamada-Kawai algorithm.
kamadaKawai :: LayoutMethod
kamadaKawai = KamadaKawai
    { kk_seed = Nothing
    , kk_nIter = 10
    , kk_const = Nothing
    , kk_epsilon = 0 }

-- | Default parameters for the LGL algorithm.
lgl :: LayoutMethod
lgl = LGL
    { lgl_nIter = 100
    , lgl_maxdelta = \x -> fromIntegral x
    , lgl_area = area
    , lgl_coolexp = 1.5
    , lgl_repulserad = \x -> fromIntegral x * area x
    , lgl_cellsize = \x -> area x ** 0.25
    }
  where
    area x = fromIntegral $ x * x

-- | Places the vertices uniform randomly on a plane.
{#fun igraph_layout_random as ^
    { `IGraph'
    , castPtr `Ptr Matrix'
    } -> `CInt' void- #}

{#fun igraph_layout_kamada_kawai as ^
    { `IGraph'                    -- ^ Graph
    , castPtr `Ptr Matrix'        -- ^ Pointer to the result matrix
    , `Bool'                      -- ^ Whether to use the seed
    , `Int'                       -- ^ The maximum number of iterations to perform
    , `Double'                    -- ^ epsilon
    , `Double'                    -- ^ kkconst
    , castPtr `Ptr Vector'        -- ^ edges weights
    , castPtr `Ptr Vector'
    , castPtr `Ptr Vector'
    , castPtr `Ptr Vector'
    , castPtr `Ptr Vector'
    } -> `CInt' void- #}

{# fun igraph_layout_lgl as ^
    { `IGraph'
    , castPtr `Ptr Matrix'
    , `Int'
    , `Double'
    , `Double'
    , `Double'
    , `Double'
    , `Double'
    , `Int'
    } -> `CInt' void- #}