Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a function to return the list of edges in a graph #17

Merged
merged 9 commits into from
Jul 30, 2022
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Notable changes to this project are documented in this file. The format is based
Breaking changes:

New features:
- Added an `edges` function that returns a list of all edges in the graph (#17 by @MaybeJustJames)
- Added `toMap` to unwrap `Graph` (#18)

Bugfixes:

Expand All @@ -19,7 +21,6 @@ Breaking changes:

New features:
- Added `Foldable` and `Traversable` instances for `Graph` (#16 by @MaybeJustJames)
- Added `toMap` to unwrap `Graph` (#18)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you put this back into the correct spot in the changelog?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this because it hasn't been released. Would you prefer a separate PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, my bad. No, let's just fix it here.


Bugfixes:

Expand Down
24 changes: 21 additions & 3 deletions src/Data/Graph.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,25 @@

module Data.Graph
( Graph
, Edge
, unfoldGraph
, fromMap
, toMap
, vertices
, edges
, lookup
, outEdges
, topologicalSort
) where

import Prelude

import Data.Bifunctor (lmap)
import Data.CatList (CatList)
import Data.CatList as CL
import Data.Foldable (class Foldable, foldl, foldr, foldMap)
import Data.List (List(..))
import Data.FoldableWithIndex (foldlWithIndex)
import Data.List (List(..), (:))
import Data.List as L
import Data.Map (Map)
import Data.Map as M
Expand All @@ -42,6 +46,9 @@ instance traversableGraph :: Traversable (Graph k) where
traverse f (Graph m) = Graph <$> (traverse (\(v /\ ks) -> (_ /\ ks) <$> (f v)) m)
sequence = traverse identity

-- | An Edge between 2 nodes in a Graph
type Edge k = { start :: k, end :: k }

-- | Unfold a `Graph` from a collection of keys and functions which label keys
-- | and specify out-edges.
unfoldGraph
Expand All @@ -54,9 +61,9 @@ unfoldGraph
-> (k -> v)
-> (k -> out k)
-> Graph k v
unfoldGraph ks label edges =
unfoldGraph ks label theEdges =
Graph (M.fromFoldable (map (\k ->
Tuple k (Tuple (label k) (L.fromFoldable (edges k)))) ks))
Tuple k (Tuple (label k) (L.fromFoldable (theEdges k)))) ks))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you revert these two changes since they're not relevant to this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 changes are to avoid shadowing the edges name

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! Makes sense. Ok.


-- | Create a `Graph` from a `Map` which maps vertices to their labels and
-- | outgoing edges.
Expand All @@ -72,6 +79,17 @@ toMap (Graph g) = g
vertices :: forall k v. Graph k v -> List v
vertices (Graph g) = map fst (M.values g)

-- | List all edges in a graph
edges :: forall k v. Graph k v -> List (Edge k)
edges (Graph g) = foldlWithIndex edges' Nil g
where
edges' :: k -> List (Edge k) -> Tuple v (List k) -> List (Edge k)
edges' src acc (_ /\ dests) =
foldl (mkEdge src) acc dests

mkEdge :: k -> List (Edge k) -> k -> List (Edge k)
mkEdge src acc dest = { start: src, end: dest } : acc

-- | Lookup a vertex by its key.
lookup :: forall k v. Ord k => k -> Graph k v -> Maybe v
lookup k (Graph g) = map fst (M.lookup k g)
Expand Down
52 changes: 42 additions & 10 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,54 @@ module Test.Main where

import Prelude

import Effect (Effect, foreachE)
import Effect.Console (logShow)
import Data.Foldable (foldr)
import Data.Graph (Graph, Edge, edges, fromMap, unfoldGraph, topologicalSort)
import Data.List (filter, length, toUnfoldable, range, (:), List(Nil))
import Data.Map (Map)
import Data.Map as M
import Data.Maybe (Maybe(Just))
import Data.Traversable (traverse)
import Data.Graph (Graph, unfoldGraph, topologicalSort)
import Data.List (filter, toUnfoldable, range, (:), List(Nil))
import Data.Tuple (Tuple)
import Data.Tuple.Nested ((/\))
import Effect (Effect, foreachE)
import Effect.Console (logShow)

-- An example graph:
-- 0
-- / \
-- 1 2
-- \/
-- 3

example1 :: Graph Int Int
example1 =
fromMap example1'
where
example1' :: Map Int (Tuple Int (List Int))
example1' =
M.fromFoldable
[ (0 /\ (0 /\ (1 : 2 : Nil)))
, (1 /\ (1 /\ (3 : Nil)))
, (2 /\ (2 /\ (3 : Nil)))
, (3 /\ (3 /\ Nil))
]

showEdge :: forall k. Show k => Edge k -> String
showEdge { start, end } =
"(" <> show start <> " --> " <> show end <> ")"

main :: Effect Unit
main = do
let double x | x * 2 < 100000 = [x * 2]
| otherwise = []
graph :: Graph Int Int
graph = unfoldGraph (range 1 100000) identity double
let
double x
| x * 2 < 100000 = [ x * 2 ]
| otherwise = []

graph :: Graph Int Int
graph = unfoldGraph (range 1 100000) identity double
foreachE (toUnfoldable (topologicalSort graph)) logShow
logShow
$ filter (_ /= 0) <<< foldr (:) Nil
<$> traverse (\n -> if n `mod` 2 == 0 then Just 0 else Just n) graph

<$> traverse (\n -> if n `mod` 2 == 0 then Just 0 else Just n) graph
logShow (length (edges graph))
logShow $ map showEdge $ edges example1