Skip to content

Commit

Permalink
Port TopologicalSort to Java (#4110)
Browse files Browse the repository at this point in the history
Part of #4030

Ports the `TopologicalSort` class to Java. 

Once again, this is a direct translation, replacing functional idioms
with either direct iteration or equivalent `Stream` functions. The only
semantic change is returning an `Optional` rather than reporting an
error when there's a cycle.
  • Loading branch information
Scott-Guest committed Mar 19, 2024
1 parent 2dbd10c commit 35e32e9
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 42 deletions.
15 changes: 8 additions & 7 deletions kore/src/main/java/org/kframework/POSet.java
Expand Up @@ -6,13 +6,11 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.apache.commons.lang3.tuple.Pair;
import org.kframework.utils.Lazy;
import org.kframework.utils.errorsystem.KEMException;
Expand Down Expand Up @@ -108,14 +106,17 @@ public Set<T> elements() {
return elementsLazy.get();
}

private List<T> computeSortedElements() {
return StreamSupport.stream(TopologicalSort.tsort(directRelations).spliterator(), false)
.toList();
private java.util.List<T> computeSortedElements() {
Optional<Stream<T>> topological = TopologicalSort.tsort(directRelations);
// We already checked for cycles during construction, so the sort should succeed
assert topological.isPresent();
return topological.get().toList();
}

private final Lazy<List<T>> sortedElementsLazy = new Lazy<>(this::computeSortedElements);
private final Lazy<java.util.List<T>> sortedElementsLazy =
new Lazy<>(this::computeSortedElements);

public List<T> sortedElements() {
public java.util.List<T> sortedElements() {
return sortedElementsLazy.get();
}

Expand Down
50 changes: 50 additions & 0 deletions kore/src/main/java/org/kframework/TopologicalSort.java
@@ -0,0 +1,50 @@
// Copyright (c) Runtime Verification, Inc. All Rights Reserved.
package org.kframework;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;

public class TopologicalSort {
/** Topologically sort based on the provided edges, unless a cycle is present. */
public static <T> Optional<Stream<T>> tsort(Iterable<Pair<T, T>> edges) {
Map<T, Set<T>> toPred = new HashMap<>();
for (Pair<T, T> edge : edges) {
if (!toPred.containsKey(edge.getLeft())) {
toPred.put(edge.getLeft(), new HashSet<>());
}
if (!toPred.containsKey(edge.getRight())) {
toPred.put(edge.getRight(), new HashSet<>());
}
toPred.get(edge.getRight()).add(edge.getLeft());
}
return tsortInternal(toPred, Stream.empty());
}

private static <T> Optional<Stream<T>> tsortInternal(Map<T, Set<T>> toPreds, Stream<T> done) {
Map<Boolean, List<Map.Entry<T, Set<T>>>> partition =
toPreds.entrySet().stream()
.collect(Collectors.partitioningBy((e) -> e.getValue().isEmpty()));
List<Map.Entry<T, Set<T>>> noPreds = partition.get(true);
Map<T, Set<T>> hasPreds =
partition.get(false).stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
if (noPreds.isEmpty()) {
if (hasPreds.isEmpty()) {
return Optional.of(done);
}
return Optional.empty();
}
Set<T> found = noPreds.stream().map(Map.Entry::getKey).collect(Collectors.toSet());
for (Map.Entry<T, Set<T>> entry : hasPreds.entrySet()) {
entry.getValue().removeAll(found);
}
return tsortInternal(hasPreds, Stream.concat(done, found.stream()));
}
}
32 changes: 0 additions & 32 deletions kore/src/main/scala/org/kframework/TopologicalSort.scala

This file was deleted.

1 change: 0 additions & 1 deletion kore/src/main/scala/org/kframework/collections.scala
Expand Up @@ -19,7 +19,6 @@ object Collections {
def immutable[T](s: java.util.Set[T]): Set[T] = s.asScala.toSet
def immutable[T](s: java.util.List[T]): Seq[T] = s.asScala
def immutable[K, V](s: java.util.Map[K, V]): Map[K, V] = s.asScala
def immutable[T](s: Array[T]): Seq[T] = s

def mutable[T](s: scala.List[T]): java.util.List[T] = s.asJava
def mutable[T](s: Seq[T]): java.util.List[T] = s.asJava
Expand Down
@@ -1,7 +1,6 @@
// Copyright (c) Runtime Verification, Inc. All Rights Reserved.
package org.kframework.compile

import collection._
import java.util
import org.kframework.attributes.Att
import org.kframework.builtin.Sorts
Expand All @@ -14,8 +13,10 @@ import org.kframework.kore._
import org.kframework.kore.KORE.KApply
import org.kframework.kore.KORE.KLabel
import org.kframework.utils.errorsystem.KEMException
import org.kframework.Collections
import org.kframework.POSet
import org.kframework.TopologicalSort._
import scala.collection._
import scala.collection.JavaConverters._

object ConfigurationInfoFromModule
Expand Down Expand Up @@ -96,7 +97,7 @@ class ConfigurationInfoFromModule(val m: Module) extends ConfigurationInfo {

private lazy val topCells = cellSorts.diff(edges.map(_._2))

private val sortedSorts: Seq[Sort] = tsort(edges).toSeq
private val sortedSorts: Seq[Sort] = Collections.immutable(edgesPoset.sortedElements())
private val sortedEdges: Seq[(Sort, Sort)] =
edges.toList.sortWith((l, r) => sortedSorts.indexOf(l._1) < sortedSorts.indexOf(r._1))
val levels: Map[Sort, Int] = sortedEdges.foldLeft(topCells.map((_, 0)).toMap) {
Expand Down

0 comments on commit 35e32e9

Please sign in to comment.