algraph-0.7.0.0: src/Data/Graph/AdjacencyList/Grid.hs
{-|
Module : Data.Graph.AdjacencyList.Grid
Description : d-dimensional cubic lattices with periodic boundary conditions
Copyright : Thodoris Papakonstantinou, 2017-2026
License : LGPL-3
Maintainer : dev@tpapak.com
Stability : experimental
Portability : POSIX
Generators for d-dimensional cubic lattices with periodic boundary conditions
(toroidal topology). A 'PBCSquareLattice' @L D@ is the Cartesian product of
@D@ cycle graphs of length @L@: \( C_L \square^D \).
Provides both directed ('graphCubicPBC', forward edges only) and undirected
('undirectedGraphCubicPBC', forward + backward edges) variants, plus coordinate
conversion between flat vertex IDs and Cartesian coordinates.
-}
module Data.Graph.AdjacencyList.Grid
( L
, D
, CVertex
, fromTuple
, toTuple
, adjacentEdges
, vertexToCVertex
, cVertexToVertex
, PBCSquareLattice (..)
, pbcEdgeIx
, gridSize
, gridNumEdges
, pbcForwardEdges
, pbcBackwardEdges
-- * Undirected cubic graph with PBC
, undirectedGraphCubicPBC
-- * Directed cubic graph with PBC
, graphCubicPBC
) where
import Data.List
import qualified Data.Map.Lazy as M
import Numeric.Natural
import Data.Graph.AdjacencyList
-- | Linear size of the lattice (number of vertices per dimension).
type L = Natural
-- | Dimensionality of the lattice (2 = square, 3 = cubic, etc.).
type D = Natural
-- | Cartesian coordinates of a lattice vertex: a list of per-dimension indices.
type CVertex = [Vertex]
data CEdge = CEdge CVertex CVertex -- ^ Cartesian representation of a Lattice Vertex
data Direction = Forward | Backward deriving (Eq, Ord, Show, Read, Bounded, Enum)
-- | A PBCSquareLattice is the Cartesian product of a cycle graph of length L
-- (C_L) => (C_L)▢^d
data PBCSquareLattice = PBCSquareLattice L D
instance Eq PBCSquareLattice where
(==) (PBCSquareLattice la da) (PBCSquareLattice lb db) =
la == la && da == db
instance Show PBCSquareLattice where
show (PBCSquareLattice l d) = "Lattice: { \n" ++
" L : " ++ show l ++ "\n" ++
" D : " ++ show d ++ "\n" ++
" numVertices : " ++ show (gridN l d) ++ "\n" ++
" numEdges : " ++ show (gridNumEdges (PBCSquareLattice l d))
-- | Undirected graph on a PBC cubic lattice (both forward and backward edges).
-- Contains @2 * D * L^D@ directed edges (two per neighbor pair).
undirectedGraphCubicPBC :: PBCSquareLattice -> Graph
undirectedGraphCubicPBC (PBCSquareLattice l d) =
let vs = gridVertices l d
neis = pbcUndirectedNeighbors l d
in createGraph vs neis
-- | Directed graph embeded in cubic lattice
graphCubicPBC :: PBCSquareLattice -> Graph
graphCubicPBC (PBCSquareLattice l d) =
let vs = gridVertices l d
neis = pbcDirectedNeighbors l d
in createGraph vs neis
-- | Number of directed (forward) edges in the lattice: @D * L^D@.
gridNumEdges :: PBCSquareLattice -> Natural
gridNumEdges (PBCSquareLattice l d) = d * (gridN l d)
gridN :: L -> D -> Natural
gridN l d = l ^ d
-- | Total number of vertices in the lattice: @L^D@.
gridSize :: PBCSquareLattice -> Natural
gridSize (PBCSquareLattice l d) = gridN l d
gridVertices :: L -> D -> [Vertex]
gridVertices l d = [1 .. (fromEnum l ^ fromEnum d)]
-- | Returns the next vertex of v in the d dimension for a grid of side l
pbcNeighbor :: Vertex -> L -> D -> Direction -> Vertex
pbcNeighbor v l d r
| r == Forward =
if not $! isBoundary v l d
then v + innerOffset l d
else v + pbcOffset l d
| r == Backward =
if not $ isBoundary (v - innerOffset l d) l d
then v - innerOffset l d
else v - pbcOffset l d
where
l' = fromEnum l
d' = fromEnum d
innerOffset :: L -> D -> Vertex
innerOffset l d = l'^(d' - 1)
pbcOffset :: L -> D -> Vertex
pbcOffset l d = - l'^d + l'^(d' - 1)
isBoundary :: Vertex -> L -> D -> Bool
isBoundary v l d = (l'^d') - (l'^(d' - 1)) - mod (v - 1) (l'^d') <= 0
-- | Given vertex returns list of nearest neighboring vertices on a Toroidal Boundary Conditions (pbc) grid
pbcDirectedNeighbors :: L -> D -> Neighbors
pbcDirectedNeighbors l d v = fmap (\d'-> pbcNeighbor v l d' Forward) [1 .. d]
-- | Given vertex returns list of nearest neighboring vertices on a Toroidal Boundary Conditions (pbc) grid
pbcUndirectedNeighbors :: L -> D -> Vertex -> [Vertex]
pbcUndirectedNeighbors l d v = (\r d'-> pbcNeighbor v l d' r)
<$> [Forward,Backward] <*> [1 .. d]
-- | Given a Vertex returns a tuple of the Cartesian product of a L sized Cycle graph
vertexToCVertex :: L -> D -> Vertex -> CVertex
vertexToCVertex l' d' v = do
let cix l n i = (mod (div (n-1) (l^(i-1))) l) + 1
out = map (cix l v) [1 .. d]
out
where l = fromEnum l'
d = fromEnum d'
-- | The reverse function of vertexToCVertex
cVertexToVertex :: L -> D -> CVertex -> Vertex
cVertexToVertex l' d' cv = do
(foldr (\t@(i,x)-> (+) ((x-1) * (l^(i-1)))) 0 $ zip [1 .. d] cv) + 1
where l = fromEnum l'
d = fromEnum d'
-- | Gives Forward vertex in a cycle graph of length L
forwardVertexInCycle :: L -> Vertex -> Vertex
forwardVertexInCycle l' v
| v == l = 1
| otherwise = v + 1
where l = fromEnum l'
-- | Gives Forward vertex in a cycle graph of length L
backwardVertexInCycle :: L -> Vertex -> Vertex
backwardVertexInCycle l' v
| v == 1 = l
| otherwise = v - 1
where l = fromEnum l'
-- | Given two edges returns if they belong to the lattice
isEdgeInCycle :: L -> Edge -> Bool
isEdgeInCycle l' (Edge a b)
| a == b - 1 = True
| a == b + 1 = True
| a == l && b == 1 = True
| b == l && a == 1 = True
| otherwise = False
where l = fromEnum l'
-- | Returns tuple (edge) giving forward vertices of given vertex on a Toroidal Boundary Conditions (pbc) grid
pbcForwardEdges :: L -> D -> Vertex -> [Edge]
pbcForwardEdges l d v = fmap (\d -> Edge v (pbcNeighbor v l d Forward)) [1 .. d]
-- | Returns tuple (edge) giving backward vertices of given vertex on a Toroidal Boundary Conditions (pbc) grid
pbcBackwardEdges :: L -> D -> Vertex -> [Edge]
pbcBackwardEdges l d v = fmap (\d -> Edge v (pbcNeighbor v l d Backward)) [1 .. d]
pbcUndirectedEdges :: L -> D -> [Edge]
pbcUndirectedEdges l d =
let nei v =
foldl'
(\ac d -> ac ++
[ Edge v (pbcNeighbor v l d Forward)
, Edge v (pbcNeighbor v l d Backward)
]
)[] [1 .. d]
in foldr (\v ac -> (nei v) ++ ac) [] $ gridVertices l d
-- | Returns tuple (edge) giving forward and backward vertices of given vertex on a Toroidal Boundary Conditions (pbc) grid
pbcAdjacentEdges :: L -> D -> Vertex -> [Edge]
pbcAdjacentEdges l d v = (\r d ->
case r of Forward -> Edge v (pbcNeighbor v l d r)
Backward -> Edge (pbcNeighbor v l d r) v
)
<$> [Forward,Backward] <*> [1 .. d]
-- | List of edges of grid with periodic boundary conditions
pbcDirectedEdges :: L -> D -> [Edge]
pbcDirectedEdges l d = (\v j-> Edge v (pbcNeighbor v l j Forward)) <$> gridVertices l d <*> [1 .. d]
-- | Index of edge of a grid with periodic boundary conditions
-- Very inefficient, better use Data.Map for lookups.
pbcEdgeIx :: L -> D -> Edge -> Maybe Int
pbcEdgeIx l d e = do
let Edge s t = e
a = vertexToCVertex l d s
b = vertexToCVertex l d t
(((a',b'),di),dist) = diff (CEdge a b)
case dist == 1 of
True -> case forwardVertexInCycle l a' == b' of
True -> Just $ ((s-1)*d') + di
False -> Just $ ((t-1)*d') + di
False -> Nothing
where
d' = fromEnum d
step (((a',b'),di'), ds) ((s,t),di)
| s == t = (((a',b'),di'), ds)
| s /= t = (((s,t),di), ds+1)
diff :: CEdge -> (((Vertex,Vertex),Int),Int)
diff (CEdge a b) = foldl step (((0,0),0),0) $ zip (zip a b) [1..d']