-
Notifications
You must be signed in to change notification settings - Fork 69
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
Implement connectedComponents and isConnected #216
base: main
Are you sure you want to change the base?
Changes from all commits
0e0479a
b0ba207
fef6f13
05a8e53
3544375
26ba817
d6bf718
c661323
bee8624
a2cbdf4
36f95b5
8e13aa5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,14 +7,15 @@ but over time many contributors helped make it much better, including (among oth | |
* [Adithya Obilisetty](mailto:[email protected]) [@adithyaov](https://github.com/adithyaov) | ||
* [Alexandre Moine](mailto:[email protected]) [@nobrakal](https://github.com/nobrakal) | ||
* [Armando Santos](mailto:[email protected]) [@bolt12](https://github.com/bolt12) | ||
* Patrick Hilhorst [@Synthetica9](https://github.com/synthetica9) | ||
* [Piotr Gawryś](mailto:[email protected]) [@Avasil](https://github.com/Avasil) | ||
|
||
If you are not on this list, it's not because your contributions are not appreciated, but | ||
If you are not on this list, it's not because your contributions are not appreciated, but | ||
because I didn't want to add your name and contact details without your consent. Please fix this | ||
by sending a PR, keeping the list alphabetical. | ||
|
||
Also see the autogenerated yet still possibly incomplete | ||
[list of contributors](https://github.com/snowleopard/alga/graphs/contributors). | ||
|
||
Thank you all for your help! | ||
Thank you all for your help! | ||
Andrey |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,7 +94,8 @@ library | |
base >= 4.7 && < 5, | ||
containers >= 0.5.5.1 && < 0.8, | ||
deepseq >= 1.3.0.1 && < 1.5, | ||
mtl >= 2.1 && < 2.3 | ||
mtl >= 2.1 && < 2.3, | ||
equivalence >= 0.3 && < 0.4 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to avoid introducing new dependencies. Could you list the functionality you use along with asymptotic complexity? |
||
if !impl(ghc >= 8.0) | ||
build-depends: semigroups >= 0.18.2 && < 0.18.4 | ||
default-language: Haskell2010 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
{-# LANGUAGE DeriveFunctor #-} | ||
{-# LANGUAGE DeriveFunctor, PartialTypeSignatures #-} | ||
|
||
{-# OPTIONS_GHC -Wno-partial-type-signatures #-} | ||
----------------------------------------------------------------------------- | ||
-- | | ||
-- Module : Algebra.Graph.NonEmpty | ||
|
@@ -50,20 +52,25 @@ module Algebra.Graph.NonEmpty ( | |
transpose, induce1, induceJust1, simplify, sparsify, sparsifyKL, | ||
|
||
-- * Graph composition | ||
box | ||
box, | ||
|
||
-- * Graph connectedness | ||
components, isConnected, | ||
) where | ||
|
||
import Control.DeepSeq | ||
import Control.Monad.State | ||
import Data.List.NonEmpty (NonEmpty (..)) | ||
import Data.Semigroup ((<>)) | ||
import Data.Traversable (for) | ||
|
||
import Algebra.Graph.Internal | ||
|
||
import qualified Algebra.Graph as G | ||
import qualified Algebra.Graph.ToGraph as T | ||
import qualified Algebra.Graph.AdjacencyMap as AM | ||
import qualified Algebra.Graph.AdjacencyIntMap as AIM | ||
import qualified Data.Equivalence.Monad as EQ | ||
import qualified Data.Graph as KL | ||
import qualified Data.IntSet as IntSet | ||
import qualified Data.List.NonEmpty as NonEmpty | ||
|
@@ -974,3 +981,67 @@ sparsifyKL n graph = KL.buildG (1, next - 1) ((n + 1, n + 2) : Exts.toList (res | |
m <- get | ||
put (m + 1) | ||
(\xs ys -> Exts.fromList [(s,m), (m,t)] <> xs <> ys) <$> s `x` m <*> m `y` t | ||
|
||
|
||
components :: Ord a => G.Graph a -> Set.Set (Graph a) | ||
components = G.foldg | ||
Set.empty | ||
(Set.singleton . Vertex) | ||
(underList (mergeEquivalent T.sharesVertex overlay) ... Set.union) | ||
connectCCs | ||
where | ||
-- TODO: move some of these definitions to src/Algebra/Graph/Internal.hs? | ||
underList :: Ord a => ([a] -> [a]) -> (Set.Set a -> Set.Set a) | ||
underList = (Set.fromList .) . (. Set.toList) | ||
|
||
pairs :: Eq a => [a] -> [(a, a)] | ||
pairs [] = [] | ||
pairs (x: xs) = ((x,) <$> xs) ++ pairs xs | ||
|
||
mergeEquivalent :: (Ord a) => (a -> a -> Bool) -> (a -> a -> a) -> ([a] -> [a]) | ||
mergeEquivalent (===) (+) xs = let | ||
-- https://github.com/quchen/articles/blob/master/2018-11-22_zipWith_const.md | ||
indices = zipWith const [0..] xs -- Because just [0..len] generates [0] when xs == []! | ||
combis = pairs indices | ||
|
||
update :: IntSet.IntSet -> (Int, Int) -> EQ.EquivM _ _ Int IntSet.IntSet | ||
update = \s (i1, i2) -> do | ||
eq <- EQ.equivalent i1 i2 | ||
if eq | ||
then pure s | ||
else do | ||
x1 <- EQ.classDesc i1 | ||
x2 <- EQ.classDesc i2 | ||
if (x1 === x2) | ||
then do | ||
EQ.equate i1 i2 | ||
pure $ IntSet.delete i1 s | ||
else | ||
pure s | ||
|
||
in EQ.runEquivM (xs !!) (+) $ do | ||
s <- foldM update (IntSet.fromList indices) combis | ||
classes <- for (IntSet.toList s) EQ.classDesc | ||
pure classes | ||
|
||
nonEmptySet :: Set.Set a -> Maybe (NonEmpty a) | ||
nonEmptySet = NonEmpty.nonEmpty . Set.toList | ||
|
||
connectCCs :: Set.Set (Graph a) -> Set.Set (Graph a) -> Set.Set (Graph a) | ||
connectCCs a b = case nonEmptySet a of | ||
Nothing -> b | ||
Just ca -> case nonEmptySet b of | ||
Nothing -> a | ||
Just cb -> Set.singleton $ overlays1 $ connect <$> ca <*> cb | ||
|
||
|
||
-- | Does the graph have exactly one connected component? | ||
-- @ | ||
-- isConnected empty == False | ||
-- isConnected $ vertex x == True | ||
-- isConnected $ vertex x + vertex y == False | ||
-- @ | ||
isConnected :: Ord a => G.Graph a -> Bool | ||
isConnected g = case Set.toList $ components g of | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you mean connected components in the sense of undirected graphs, right? If yes, this should be clarified and also moved to a more fitting module, e.g. |
||
[_] -> True | ||
_ -> False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These spaces are important for rendering, please undo whitespace changes in this file.