packages feed

hgeometry-combinatorial-0.14: src/Algorithms/FloydWarshall.hs

--------------------------------------------------------------------------------
-- |
-- Module      :  Data.Geometry.Polygon.Core
-- Copyright   :  (C) David Himmelstrup
-- License     :  see the LICENSE file
-- Maintainer  :  David Himmelstrup
--
-- Implementation of Floyd-Warshall shortest path algorithm.
--
-- See Wikipedia article for details: https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
--
--------------------------------------------------------------------------------
module Algorithms.FloydWarshall
  ( mkIndex
  , mkGraph
  , floydWarshall
  ) where

import           Control.Monad               (forM_, when)
import           Control.Monad.ST            (ST)
import           Data.Vector.Unboxed.Mutable as V (MVector, length, replicate, unsafeRead,
                                                   unsafeWrite, Unbox)

-- | \( O(n^3) \)
floydWarshall :: (Unbox a, Fractional a, Ord a) => Int -> MVector s (a, Int) -> ST s ()
floydWarshall n graph = do
    let nSq = V.length graph
    when (n*n /= nSq) $ error "Bad bounds"
    forM_ [0 .. n-1] $ \k ->
      forM_ [0 .. n-1] $ \i ->
        forM_ [0 .. n-1] $ \j -> do
          (distIJ, _) <- access (i,j)
          (distIK, pathIK) <- access (i,k)
          (distKJ, _) <- access (k,j)
          let indirectDist = distIK + distKJ
          when (distIJ > indirectDist+indirectDist*eps && distIJ > distIK && distIJ > distKJ) $
            put (i,j) (indirectDist, pathIK)
  where
    access idx = V.unsafeRead graph (mkIndex n idx)
    put idx e = V.unsafeWrite graph (mkIndex n idx) e
    eps = 1e-10 -- When two paths are nearly the same length, pick the one with the fewest segments.

-- | Compute the index of an element in a given range.
mkIndex :: Num a => a -> (a, a) -> a
mkIndex n (i,j) = i*n+j

-- | Construct a weighted graph from \(n\) vertices, a max bound, and a list of weighted edges.
mkGraph :: (Unbox a, Num a) => Int -> a -> [(Int,Int,a)] -> ST s (MVector s (a, Int))
mkGraph n maxValue edges = do
  graph <- V.replicate (n*n) (maxValue, maxBound)
  forM_ [0..n-1] $ \v -> do
    unsafeWrite graph (mkIndex n (v,v)) (0, v)
  forM_ edges $ \(i,j,cost) -> do
    unsafeWrite graph (mkIndex n (i,j)) (cost, j)
    unsafeWrite graph (mkIndex n (j,i)) (cost, i)
  return graph