public UndirectedGraph(ImmutableArray <HashSet <Vertex> > adjacencies) { for (int i = 0; i < adjacencies.Length; ++i) { var v = Vertex.Nth(i); foreach (var w in adjacencies[v.index]) { Debug.Assert(v != w); Debug.Assert(adjacencies[w.index].Contains(v)); } } itsAdjacencies = adjacencies; }
// Iterate connected vertices, lowest degree first. // drop=N: omit last N vertices public static IEnumerable <Vertex> Ordering(UndirectedGraph graph, int drop) { Debug.Assert(drop >= 0); const int NO_PRIORITY = -1; int[] priorityPerVertex = new int[graph.Order]; var maxPriority = 0; var numLeftToPick = 0; foreach (int i in Enumerable.Range(0, graph.Order)) { var c = Vertex.Nth(i); var degree = graph.Degree(c); if (degree > 0) { var priority = degree; maxPriority = Math.Max(maxPriority, priority); priorityPerVertex[i] = degree; numLeftToPick += 1; } } // Possible values of priority_per_vertex: // no_priority: when yielded or if unconnected // 0..maxPriority: candidates still queued with priority (degree - #of yielded neighbours) var q = new PriorityQueue(maxPriority); foreach (var(c, p) in priorityPerVertex.Select((p, i) => (Vertex.Nth(i), p))) { if (p > 0) { q.Put(priority: p, element: c); } } numLeftToPick -= drop; while (numLeftToPick >= 0) { numLeftToPick -= 1; var i = q.Pop(); while (priorityPerVertex[i.index] == NO_PRIORITY) { // was requeued with a more urgent priority and therefore already picked i = q.Pop(); } Debug.Assert(priorityPerVertex[i.index] >= 0); priorityPerVertex[i.index] = NO_PRIORITY; yield return(i); foreach (var v in graph.Neighbours(i)) { var oldPriority = priorityPerVertex[v.index]; if (oldPriority != NO_PRIORITY) { // Since this is an unvisited neighbour of a vertex just being picked, // its priority can't be down to the minimum. Debug.Assert(oldPriority > 0); // Requeue with a more urgent priority, but don't bother to remove // the original entry - it will be skipped if it's reached at all. priorityPerVertex[v.index] = oldPriority - 1; q.Put(priority: oldPriority - 1, element: v); } } } }
public static void Visit(UndirectedGraph graph, IReporter reporter, Choice choice, 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.Count >= 1); if (candidates.Count == 1) { // Same logic as below, stripped down var v = candidates.First(); var neighbours = graph.Neighbours(v); if (CollectionsUtil.AreDisjoint(neighbours, excluded)) { reporter.Record(CollectionsUtil.Append(cliqueInProgress, v)); } return; } Vertex pivot; var remainingCandidates = new Vertex[candidates.Count]; var remainingCandidateCount = 0; // Quickly handle locally unconnected candidates while finding pivot const int INVALID = int.MaxValue; pivot = Vertex.Nth(INVALID); var seenLocalDegree = 0; foreach (var v in candidates) { var neighbours = graph.Neighbours(v); var localDegree = CollectionsUtil.IntersectionSize(neighbours, candidates); if (localDegree == 0) { // Same logic as below, stripped down if (CollectionsUtil.AreDisjoint(neighbours, excluded)) { reporter.Record(CollectionsUtil.Append(cliqueInProgress, v)); } } else { if (seenLocalDegree < localDegree) { seenLocalDegree = localDegree; pivot = v; } remainingCandidates[remainingCandidateCount] = v; remainingCandidateCount += 1; } } if (seenLocalDegree == 0) { return; } Debug.Assert(pivot.index != INVALID); if (choice == Choice.MaxDegreeLocalX) { foreach (var v in excluded) { var neighbours = graph.Neighbours(v); var localDegree = CollectionsUtil.IntersectionSize(neighbours, candidates); if (seenLocalDegree < localDegree) { seenLocalDegree = localDegree; pivot = v; } } } for (var i = 0; i < remainingCandidateCount; ++i) { var v = remainingCandidates[i]; var neighbours = graph.Neighbours(v); Debug.Assert(neighbours.Any()); if (!neighbours.Contains(pivot)) { var removed = candidates.Remove(v); Debug.Assert(removed); var neighbouringCandidates = CollectionsUtil.Intersection(neighbours, candidates); if (neighbouringCandidates.Any()) { var neighbouringExcluded = CollectionsUtil.Intersection(neighbours, excluded); Visit(graph, reporter, choice, neighbouringCandidates, neighbouringExcluded, CollectionsUtil.Append(cliqueInProgress, v)); } else if (CollectionsUtil.AreDisjoint(neighbours, excluded)) { reporter.Record(CollectionsUtil.Append(cliqueInProgress, v)); } var added = excluded.Add(v); Debug.Assert(added); } } }