private static void Visit(UndirectedGraph graph, IReporter reporter, ISet <Vertex> candidates, ISet <Vertex> excluded, ImmutableArray <Vertex> cliqueInProgress) { Debug.Assert(candidates.All(v => graph.Degree(v) > 0)); Debug.Assert(excluded.All(v => graph.Degree(v) > 0)); Debug.Assert(!candidates.Overlaps(excluded)); Debug.Assert(candidates.Any()); while (candidates.Any()) { var v = CollectionsUtil.PopArbitrary(candidates); var neighbours = graph.Neighbours(v); var neighbouringCandidates = CollectionsUtil.Intersection(candidates, neighbours); if (neighbouringCandidates.Any()) { var neighbouringExcluded = CollectionsUtil.Intersection(excluded, neighbours); Visit(graph, reporter, neighbouringCandidates, neighbouringExcluded, CollectionsUtil.Append(cliqueInProgress, v)); } else if (CollectionsUtil.AreDisjoint(excluded, neighbours)) { reporter.Record(CollectionsUtil.Append(cliqueInProgress, v)); } var added = excluded.Add(v); Debug.Assert(added); } }
public void AreDisjoint() { var empty = new HashSet <int> { }; var one = new HashSet <int> { 1 }; var two = new HashSet <int> { 1, 2 }; var six = new HashSet <int> { 0, 1, 2, 3, 4, 5 }; Assert.That(CollectionsUtil.AreDisjoint(empty, one)); Assert.That(CollectionsUtil.AreDisjoint(one, empty)); Assert.That(CollectionsUtil.AreDisjoint(empty, two)); Assert.That(CollectionsUtil.AreDisjoint(two, empty)); Assert.That(CollectionsUtil.AreDisjoint(empty, six)); Assert.That(CollectionsUtil.AreDisjoint(six, empty)); Assert.That(!CollectionsUtil.AreDisjoint(one, two)); Assert.That(!CollectionsUtil.AreDisjoint(two, one)); Assert.That(!CollectionsUtil.AreDisjoint(one, six)); Assert.That(!CollectionsUtil.AreDisjoint(six, one)); Assert.That(!CollectionsUtil.AreDisjoint(two, six)); Assert.That(!CollectionsUtil.AreDisjoint(six, two)); Assert.That(!CollectionsUtil.AreDisjoint(one, one)); Assert.That(!CollectionsUtil.AreDisjoint(two, two)); Assert.That(!CollectionsUtil.AreDisjoint(six, six)); }
public static void Explore(UndirectedGraph graph, IReporter finalReporter) { var scheduler = TaskScheduler.Default; int sent = 0; int received = 0; // Step 3: collect results. var collect = new ActionBlock <ImmutableArray <Vertex> >(clique => finalReporter.Record(clique)); // Step 2: visit vertices. var reporter = new NestedReporter(collect); int waitgroup = 1; void completion(Task _) { if (Interlocked.Decrement(ref waitgroup) == 0) { reporter.Close(); collect.Complete(); } } var excluded = new HashSet <Vertex>(); var visit = new ActionBlock <Vertex>(v => { var neighbours = graph.Neighbours(v); Debug.Assert(neighbours.Any()); var neighbouringCandidates = CollectionsUtil.Difference(neighbours, excluded); if (neighbouringCandidates.Any()) { var neighbouringExcluded = CollectionsUtil.Intersection(excluded, neighbours); _ = Interlocked.Increment(ref waitgroup); _ = Task.Run(delegate { Pivot.Visit(graph, reporter, Pivot.Choice.MaxDegreeLocal, neighbouringCandidates, neighbouringExcluded, ImmutableArray.Create(v)); }).ContinueWith(completion, scheduler); } else { Debug.Assert(!CollectionsUtil.AreDisjoint(neighbours, excluded)); } var added = excluded.Add(v); Debug.Assert(added); ++received; }); _ = visit.Completion.ContinueWith(completion, scheduler); // Step 1: feed vertices in order. _ = Task.Run(delegate { foreach (var v in Degeneracy.Ordering(graph, drop: 1)) { while (!visit.Post(v)) { throw new InvalidOperationException("Post failed"); } ++sent; } visit.Complete(); }); collect.Completion.Wait(); if (sent != received) { throw new InvalidOperationException($"{sent} sent <> {received} received"); } }