Skip to content

Commit 1a38677

Browse files
authored
Unbox some more graph stuff (#543)
* Replace boxed arrays of `Int` with unboxed ones. * Make a `zipWith` able to fuse. * Use `fmap` in `outdegree` rather than a custom function for mapping with an index we don't need anyway. * Use `.Safe` array modules in `Data.Graph`.
1 parent 4952822 commit 1a38677

File tree

2 files changed

+49
-16
lines changed

2 files changed

+49
-16
lines changed

Data/Graph.hs

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
{-# LANGUAGE DeriveDataTypeable #-}
55
{-# LANGUAGE DeriveGeneric #-}
66
{-# LANGUAGE StandaloneDeriving #-}
7+
# if __GLASGOW_HASKELL__ >= 710
8+
{-# LANGUAGE Safe #-}
9+
# else
710
{-# LANGUAGE Trustworthy #-}
11+
# endif
812
#endif
913

1014
#include "containers.h"
@@ -86,13 +90,14 @@ module Data.Graph (
8690

8791
) where
8892

89-
#if __GLASGOW_HASKELL__
90-
# define USE_ST_MONAD 1
91-
#endif
92-
9393
#if USE_ST_MONAD
9494
import Control.Monad.ST
95-
import Data.Array.ST (STUArray, newArray, readArray, writeArray)
95+
import Data.Array.ST.Safe (newArray, readArray, writeArray)
96+
# if USE_UNBOXED_ARRAYS
97+
import Data.Array.ST.Safe (STUArray)
98+
# else
99+
import Data.Array.ST.Safe (STArray)
100+
# endif
96101
#else
97102
import Data.IntSet (IntSet)
98103
import qualified Data.IntSet as Set
@@ -110,6 +115,12 @@ import Data.Foldable as F
110115
import Control.DeepSeq (NFData(rnf))
111116
import Data.Maybe
112117
import Data.Array
118+
#if USE_UNBOXED_ARRAYS
119+
import qualified Data.Array.Unboxed as UA
120+
import Data.Array.Unboxed ( UArray )
121+
#else
122+
import qualified Data.Array as UA
123+
#endif
113124
import Data.List
114125
#if MIN_VERSION_base(4,9,0)
115126
import Data.Functor.Classes
@@ -121,6 +132,8 @@ import Data.Data (Data)
121132
import Data.Typeable
122133
#endif
123134

135+
-- Make sure we don't use Integer by mistake.
136+
default ()
124137

125138
-------------------------------------------------------------------------
126139
-- -
@@ -269,6 +282,10 @@ type Bounds = (Vertex, Vertex)
269282
-- | An edge from the first vertex to the second.
270283
type Edge = (Vertex, Vertex)
271284

285+
#if !USE_UNBOXED_ARRAYS
286+
type UArray i a = Array i a
287+
#endif
288+
272289
-- | Returns the list of vertices in the graph.
273290
--
274291
-- ==== __Examples__
@@ -289,9 +306,6 @@ vertices = indices
289306
edges :: Graph -> [Edge]
290307
edges g = [ (v, w) | v <- vertices g, w <- g!v ]
291308

292-
mapT :: (Vertex -> a -> b) -> Array Vertex a -> Array Vertex b
293-
mapT f t = array (bounds t) [ (,) v (f v (t!v)) | v <- indices t ]
294-
295309
-- | Build a graph from a list of edges.
296310
--
297311
-- Warning: This function will cause a runtime exception if a vertex in the edge
@@ -324,8 +338,11 @@ reverseE g = [ (w, v) | (v, w) <- edges g ]
324338
--
325339
-- > outdegree (buildG (0,2) [(0,1), (1,2)]) == array (0,2) [(0,1),(1,1),(2,0)]
326340
outdegree :: Graph -> Array Vertex Int
327-
outdegree = mapT numEdges
328-
where numEdges _ ws = length ws
341+
-- This is bizarrely lazy. We build an array filled with thunks, instead
342+
-- of actually calculating anything. This is the historical behavior, and I
343+
-- suppose someone *could* be relying on it, but it might be worth finding
344+
-- out. Note that we *can't* be so lazy with indegree.
345+
outdegree = fmap length
329346

330347
-- | A table of the count of edges into each node.
331348
--
@@ -475,7 +492,11 @@ chop (Node v ts : us)
475492

476493
-- Use the ST monad if available, for constant-time primitives.
477494

495+
#if USE_UNBOXED_ARRAYS
478496
newtype SetM s a = SetM { runSetM :: STUArray s Vertex Bool -> ST s a }
497+
#else
498+
newtype SetM s a = SetM { runSetM :: STArray s Vertex Bool -> ST s a }
499+
#endif
479500

480501
instance Monad (SetM s) where
481502
return = pure
@@ -555,10 +576,14 @@ preorderF' ts = foldr (.) id $ map preorder' ts
555576
preorderF :: Forest a -> [a]
556577
preorderF ts = preorderF' ts []
557578

558-
tabulate :: Bounds -> [Vertex] -> Array Vertex Int
559-
tabulate bnds vs = array bnds (zipWith (,) vs [1..])
579+
tabulate :: Bounds -> [Vertex] -> UArray Vertex Int
580+
tabulate bnds vs = UA.array bnds (zipWith (flip (,)) [1..] vs)
581+
-- Why zipWith (flip (,)) instead of just using zip with the
582+
-- arguments in the other order? We want the [1..] to fuse
583+
-- away, and these days that only happens when it's the first
584+
-- list argument.
560585

561-
preArr :: Bounds -> Forest Vertex -> Array Vertex Int
586+
preArr :: Bounds -> Forest Vertex -> UArray Vertex Int
562587
preArr bnds = tabulate bnds . preorderF
563588

564589
------------------------------------------------------------
@@ -622,6 +647,9 @@ cross g pre post = mapT select g
622647
forward :: Graph -> Graph -> Table Int -> Graph
623648
forward g tree' pre = mapT select g
624649
where select v ws = [ w | w <- ws, pre!v < pre!w ] \\ tree' ! v
650+
651+
mapT :: (Vertex -> a -> b) -> Array Vertex a -> Array Vertex b
652+
mapT f t = array (bounds t) [ (,) v (f v (t!v)) | v <- indices t ]
625653
-}
626654

627655
------------------------------------------------------------
@@ -662,10 +690,10 @@ bcc g = (concat . map bicomps . map (do_label g dnum)) forest
662690
where forest = dff g
663691
dnum = preArr (bounds g) forest
664692

665-
do_label :: Graph -> Array Vertex Int -> Tree Vertex -> Tree (Vertex,Int,Int)
666-
do_label g dnum (Node v ts) = Node (v,dnum!v,lv) us
693+
do_label :: Graph -> UArray Vertex Int -> Tree Vertex -> Tree (Vertex,Int,Int)
694+
do_label g dnum (Node v ts) = Node (v, dnum UA.! v, lv) us
667695
where us = map (do_label g dnum) ts
668-
lv = minimum ([dnum!v] ++ [dnum!w | w <- g!v]
696+
lv = minimum ([dnum UA.! v] ++ [dnum UA.! w | w <- g!v]
669697
++ [lu | Node (_,_,lu) _ <- us])
670698

671699
bicomps :: Tree (Vertex,Int,Int) -> Forest [Vertex]

include/containers.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,9 @@
3333
#define DEFINE_PATTERN_SYNONYMS 1
3434
#endif
3535

36+
#ifdef __GLASGOW_HASKELL__
37+
# define USE_ST_MONAD 1
38+
# define USE_UNBOXED_ARRAYS 1
39+
#endif
40+
3641
#endif

0 commit comments

Comments
 (0)