toysolver-0.9.0: src/ToySolver/Graph/Base.hs
{-# LANGUAGE FlexibleContexts #-}
-----------------------------------------------------------------------------
-- |
-- Module : ToySolver.Graph.Base
-- Copyright : (c) Masahiro Sakai 2020
-- License : BSD-style
--
-- Maintainer : masahiro.sakai@gmail.com
-- Stability : provisional
-- Portability : non-portable
--
-----------------------------------------------------------------------------
module ToySolver.Graph.Base
(
-- * Graph data types
EdgeLabeledGraph
, Graph
, Vertex
, VertexSet
, Edge
-- * Conversion
-- ** Directed graphs
, graphFromEdges
, graphFromEdgesWith
, graphToEdges
-- ** Undirected graphs
, graphFromUnorderedEdges
, graphFromUnorderedEdgesWith
, graphToUnorderedEdges
-- * Operations
, converseGraph
, complementGraph
, complementSimpleGraph
-- * Properties
, numVertexes
, isSimpleGraph
, isIndependentSet
, isIndependentSetOf
, isCliqueOf
) where
import Control.Monad
import Data.Array.IArray
import Data.Array.ST
import qualified Data.IntMap.Lazy as IntMap
import Data.IntMap.Lazy (IntMap)
import qualified Data.IntSet as IntSet
import Data.IntSet (IntSet)
import Data.Maybe (maybeToList)
import GHC.Stack (HasCallStack)
-- | Labelled directed graph without multiple edges
--
-- We also represent undirected graphs as symmetric directed graphs.
type EdgeLabeledGraph a = Array Vertex (IntMap a)
-- | Directed graph without multiple edges
--
-- We also represent undirected graphs as symmetric directed graphs.
type Graph = EdgeLabeledGraph ()
-- | Vertex data type
type Vertex = Int
-- | Set of vertexes
type VertexSet = IntSet
-- | Edge data type
type Edge a = (Vertex, Vertex, a)
-- | Set of edges of directed graph
graphToEdges :: EdgeLabeledGraph a -> [Edge a]
graphToEdges g = do
(node1, nodes) <- assocs g
(node2, a) <- IntMap.toList nodes
return (node1, node2, a)
-- | Construct a directed graph from edges.
--
-- If there are multiple edges with the same starting and ending
-- vertexes, the last label is used.
graphFromEdges :: HasCallStack => Int -> [Edge a] -> EdgeLabeledGraph a
graphFromEdges = graphFromEdgesWith const
-- | Construct a directed graph from edges.
--
-- If there are multiple edges with the same starting and ending
-- vertexes, the labels are combined using the given function.
graphFromEdgesWith :: HasCallStack => (a -> a -> a) -> Int -> [Edge a] -> EdgeLabeledGraph a
graphFromEdgesWith _ n _ | n < 0 = error "graphFromEdgesWith: number of vertexes should be non-negative"
graphFromEdgesWith f n es = runSTArray $ do
g <- newArray (0, n-1) IntMap.empty
forM_ es $ \(node1, node2, a) -> do
m <- readArray g node1
writeArray g node1 $! IntMap.insertWith f node2 a m
return g
-- | Set of edges of undirected graph represented as a symmetric directed graph.
graphToUnorderedEdges :: EdgeLabeledGraph a -> [Edge a]
graphToUnorderedEdges g = do
(node1, nodes) <- assocs g
case IntMap.splitLookup node1 nodes of
(_, m, nodes2) ->
[(node1, node1, a) | a <- maybeToList m] ++
[(node1, node2, a) | (node2, a) <- IntMap.toList nodes2]
-- | Construct a symmetric directed graph from unordered edges.
--
-- If there are multiple edges with the same starting and ending
-- vertexes, the last label is used.
graphFromUnorderedEdges :: HasCallStack => Int -> [Edge a] -> EdgeLabeledGraph a
graphFromUnorderedEdges = graphFromUnorderedEdgesWith const
-- | Construct a symmetric directed graph from unordered edges.
--
-- If there are multiple edges with the same starting and ending
-- vertexes, the labels are combined using the given function.
graphFromUnorderedEdgesWith :: HasCallStack => (a -> a -> a) -> Int -> [Edge a] -> EdgeLabeledGraph a
graphFromUnorderedEdgesWith _ n _ | n < 0 = error "graphFromUnorderedEdgesWith: number of vertexes should be non-negative"
graphFromUnorderedEdgesWith f n es = runSTArray $ do
a <- newArray (0, n-1) IntMap.empty
let ins i x l = do
m <- readArray a i
writeArray a i $! IntMap.insertWith f x l m
forM_ es $ \(node1, node2, a) -> do
ins node1 node2 a
unless (node1 == node2) $ ins node2 node1 a
return a
-- | Converse of a graph.
--
-- It returns another directed graph on the same set of vertices with all of the edges reversed.
-- This is also called /transpose/ or /reverse/ of a graph.
converseGraph :: EdgeLabeledGraph a -> EdgeLabeledGraph a
converseGraph g = graphFromEdges (numVertexes g) [(n2, n1, l) | (n1, n2, l) <- graphToEdges g]
-- | Complement of a graph
--
-- Note that applying it to a graph with no self-loops results in a graph with self-loops on all vertices.
complementGraph :: EdgeLabeledGraph a -> EdgeLabeledGraph ()
complementGraph g = array (bounds g) [(node, toAllNodes IntMap.\\ outEdges) | (node, outEdges) <- assocs g]
where
toAllNodes = IntMap.fromAscList [(node, ()) | node <- indices g]
-- | Complement of a simple graph
--
-- It ignores self-loops in the input graph and also does not add self-loops to the output graph.
complementSimpleGraph :: EdgeLabeledGraph a -> EdgeLabeledGraph ()
complementSimpleGraph g = array (bounds g) [(node, IntMap.delete node toAllNodes IntMap.\\ outEdges) | (node, outEdges) <- assocs g]
where
toAllNodes = IntMap.fromAscList [(node, ()) | node <- indices g]
-- | Number of vertexes of a graph
numVertexes :: EdgeLabeledGraph a -> Int
numVertexes g =
case bounds g of
(lb, ub)
| lb /= 0 -> error "numVertexes: lower bound should be 0"
| otherwise -> ub + 1
-- | A graph is /simple/ if it contains no self-loops.
isSimpleGraph :: EdgeLabeledGraph a -> Bool
isSimpleGraph g = and [v `IntMap.notMember` es | (v, es) <- assocs g]
-- | Alias of 'isIndependentSetOf'
{-# DEPRECATED isIndependentSet "Use isIndependentSetOf instead" #-}
isIndependentSet :: EdgeLabeledGraph a -> VertexSet -> Bool
isIndependentSet = flip isIndependentSetOf
-- | An independent set of a graph is a set of vertices such that no two vertices in the set are adjacent.
--
-- This function ignores self-loops in the input graph.
isIndependentSetOf :: VertexSet -> EdgeLabeledGraph a -> Bool
isIndependentSetOf s g = null $ do
(node1, node2, _) <- graphToUnorderedEdges g
guard $ node1 `IntSet.member` s
guard $ node2 `IntSet.member` s
return ()
-- | A clique of a graph is a subset of vertices such that every two distinct vertices in the clique are adjacent.
isCliqueOf :: VertexSet -> EdgeLabeledGraph a -> Bool
isCliqueOf s g = all (\node -> IntSet.delete node s `IntSet.isSubsetOf` IntMap.keysSet (g ! node)) (IntSet.toList s)