Commit 24eebe3e by Michael Schmid

Binary dec tree and nfj creation finished

parent a15a466e
...@@ -2,15 +2,14 @@ package mvd.jester.model; ...@@ -2,15 +2,14 @@ package mvd.jester.model;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.jgrapht.Graphs; import org.jgrapht.Graphs;
import org.jgrapht.alg.shortestpath.AllDirectedPaths;
import org.jgrapht.experimental.dag.DirectedAcyclicGraph; import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DefaultEdge;
import mvd.jester.utils.BinaryDecompositionTree; import mvd.jester.utils.BinaryDecompositionTree;
...@@ -241,65 +240,67 @@ public class DagTask implements Task { ...@@ -241,65 +240,67 @@ public class DagTask implements Task {
public static BinaryDecompositionTree<Job> createDecompositionTree( public static BinaryDecompositionTree<Job> createDecompositionTree(
DirectedAcyclicGraph<Job, DefaultEdge> jobDag, LinkedList<Job> forkNodes, DirectedAcyclicGraph<Job, DefaultEdge> jobDag, LinkedList<Job> forkNodes,
LinkedList<Job> joinNodes) { LinkedList<Job> joinNodes) {
Job source = forkNodes.getFirst();
Job sink = joinNodes.getLast();
Job current = source;
BinaryDecompositionTree<Job> tree = new BinaryDecompositionTree<>(); BinaryDecompositionTree<Job> tree = new BinaryDecompositionTree<>();
DagUtils.constructTree(jobDag, null, current, tree, forkNodes, joinNodes, sink); Optional<Job> source = Optional.empty();
Optional<Job> sink = Optional.empty();
if (forkNodes.size() > 0) {
source = Optional.of(forkNodes.getFirst());
sink = Optional.of(joinNodes.getLast());
} else {
boolean firstRun = true;
for (Job j : jobDag) {
if (firstRun) {
firstRun = false;
source = Optional.of(j);
}
sink = Optional.of(j);
}
}
if (source.isPresent() && sink.isPresent()) {
Job current = source.get();
return tree; DagUtils.treeDec(jobDag, null, current, tree, forkNodes, joinNodes, sink.get());
} }
private static Node<Job> constructTree(DirectedAcyclicGraph<Job, DefaultEdge> jobDag, return tree;
Node<Job> parent, Job current, BinaryDecompositionTree<Job> tree,
LinkedList<Job> forkVertices, LinkedList<Job> joinVertices, Job sink) {
if (forkVertices.contains(current)) {
Job join = findJoin(jobDag, current, sink, joinVertices);
if (jobDag.outDegreeOf(join) > 1) {
throw new RuntimeException("Join und Fork in Einem! Ohoh!");
} }
Node<Job> forkNode = new Node<Job>(parent, NodeType.SEQ); private static Node<Job> treeDec(DirectedAcyclicGraph<Job, DefaultEdge> jobDag,
Node<Job> parentNode, Job currentJob, BinaryDecompositionTree<Job> tree,
LinkedList<Job> forkVertices, LinkedList<Job> joinVertices, Job sinkJob) {
if (forkVertices.contains(currentJob)) {
Job forkJob = currentJob;
Job joinJob = findJoin(jobDag, forkJob, sinkJob, joinVertices);
Node<Job> seqForkNode = new Node<Job>(parentNode, NodeType.SEQ);
seqForkNode.setLeftNode(new Node<Job>(seqForkNode, forkJob));
if (tree.isEmpty()) { if (tree.isEmpty()) {
tree.setRoot(forkNode); tree.setRoot(seqForkNode);
}
forkNode.setLeftNode(new Node<Job>(forkNode, current));
final Node<Job> continuationNode;
if (!tree.contains(join)) {
forkNode.setRightNode(new Node<Job>(forkNode, NodeType.SEQ));
Node<Job> joinNode = forkNode.getRightNode();
Iterator<Job> successorIterator =
Graphs.successorListOf(jobDag, join).iterator();
if (successorIterator.hasNext()) {
Job successor = successorIterator.next();
if (!tree.contains(successor)) {
joinNode.setRightNode(new Node<>(joinNode, NodeType.SEQ));
Node<Job> successorNode = joinNode.getRightNode();
successorNode.setLeftNode(new Node<>(successorNode, join));
Node<Job> subTree = constructTree(jobDag, successorNode, successor,
tree, forkVertices, joinVertices, sink);
successorNode.setRightNode(subTree);
} else {
joinNode.setRightNode(new Node<>(joinNode, join));
} }
final Node<Job> parContinuationNode;
if (!tree.contains(joinJob)) {
Node<Job> seqJoinNode =
seqForkNode.setRightNode(new Node<Job>(seqForkNode, NodeType.SEQ));
Node<Job> subTreeOfJoin = treeDec(jobDag, seqJoinNode, joinJob, tree,
forkVertices, joinVertices, sinkJob);
seqJoinNode.setRightNode(subTreeOfJoin);
parContinuationNode =
seqJoinNode.setLeftNode(new Node<Job>(seqJoinNode, NodeType.PAR));
} else { } else {
joinNode.setRightNode(new Node<>(joinNode, join)); parContinuationNode =
} seqForkNode.setRightNode(new Node<Job>(seqForkNode, NodeType.PAR));
joinNode.setLeftNode(new Node<>(joinNode, NodeType.PAR));
continuationNode = joinNode.getLeftNode();
} else {
forkNode.setRightNode(new Node<>(forkNode, NodeType.PAR));
continuationNode = forkNode.getRightNode();
} }
Node<Job> successorParent = continuationNode;
List<Job> successors = Graphs.successorListOf(jobDag, current); Node<Job> successorParent = parContinuationNode;
List<Job> successors = Graphs.successorListOf(jobDag, currentJob);
// create leftSide of joinNode // create leftSide of joinNode
for (int i = 0; i < successors.size(); ++i) { for (int i = 0; i < successors.size(); ++i) {
Job succ = successors.get(i); Job succ = successors.get(i);
Node<Job> thisNode = constructTree(jobDag, successorParent, succ, tree, Node<Job> thisNode = treeDec(jobDag, successorParent, succ, tree, forkVertices,
forkVertices, joinVertices, sink); joinVertices, sinkJob);
if (i == successors.size() - 1) { if (i == successors.size() - 1) {
successorParent.setRightNode(thisNode); successorParent.setRightNode(thisNode);
} else if (i == successors.size() - 2) { } else if (i == successors.size() - 2) {
...@@ -311,11 +312,93 @@ public class DagTask implements Task { ...@@ -311,11 +312,93 @@ public class DagTask implements Task {
} }
} }
return forkNode; return seqForkNode;
} else { } else {
return new Node<>(parent, current); List<Job> successorList = Graphs.successorListOf(jobDag, currentJob);
if (successorList.size() > 0) {
Job successor = successorList.get(0);
if (!tree.contains(successor)) {
Node<Job> seqJobNode = new Node<Job>(parentNode, NodeType.SEQ);
if (tree.isEmpty()) {
tree.setRoot(seqJobNode);
}
seqJobNode.setLeftNode(new Node<Job>(seqJobNode, currentJob));
Node<Job> contNode = treeDec(jobDag, seqJobNode, successor, tree,
forkVertices, joinVertices, sinkJob);
seqJobNode.setRightNode(contNode);
return seqJobNode;
} }
} }
Node<Job> jobNode = new Node<Job>(parentNode, currentJob);
if (tree.isEmpty()) {
tree.setRoot(jobNode);
}
return jobNode;
}
}
// private static Node<Job> constructTree(DirectedAcyclicGraph<Job, DefaultEdge> jobDag,
// Node<Job> parent, Job currentFork, BinaryDecompositionTree<Job> tree,
// LinkedList<Job> forkVertices, LinkedList<Job> joinVertices, Job sink) {
// if (forkVertices.contains(currentFork)) {
// Job join = findJoin(jobDag, currentFork, sink, joinVertices);
// Node<Job> forkNode = new Node<Job>(parent, NodeType.SEQ);
// if (tree.isEmpty()) {
// tree.setRoot(forkNode);
// }
// forkNode.setLeftNode(new Node<Job>(forkNode, currentFork));
// final Node<Job> continuationNode;
// if (!tree.contains(join)) {
// forkNode.setRightNode(new Node<Job>(forkNode, NodeType.SEQ));
// Node<Job> joinNode = forkNode.getRightNode();
// Iterator<Job> successorIterator =
// Graphs.successorListOf(jobDag, join).iterator();
// if (successorIterator.hasNext()) {
// Job successor = successorIterator.next();
// if (!tree.contains(successor)) {
// joinNode.setRightNode(new Node<>(joinNode, NodeType.SEQ));
// Node<Job> successorNode = joinNode.getRightNode();
// successorNode.setLeftNode(new Node<>(successorNode, join));
// Node<Job> subTree = constructTree(jobDag, successorNode, successor,
// tree, forkVertices, joinVertices, sink);
// successorNode.setRightNode(subTree);
// } else {
// joinNode.setRightNode(new Node<>(joinNode, join));
// }
// } else {
// joinNode.setRightNode(new Node<>(joinNode, join));
// }
// joinNode.setLeftNode(new Node<>(joinNode, NodeType.PAR));
// continuationNode = joinNode.getLeftNode();
// } else {
// forkNode.setRightNode(new Node<>(forkNode, NodeType.PAR));
// continuationNode = forkNode.getRightNode();
// }
// Node<Job> successorParent = continuationNode;
// List<Job> successors = Graphs.successorListOf(jobDag, currentFork);
// // create leftSide of joinNode
// for (int i = 0; i < successors.size(); ++i) {
// Job succ = successors.get(i);
// Node<Job> thisNode = constructTree(jobDag, successorParent, succ, tree,
// forkVertices, joinVertices, sink);
// if (i == successors.size() - 1) {
// successorParent.setRightNode(thisNode);
// } else if (i == successors.size() - 2) {
// successorParent.setLeftNode(thisNode);
// } else {
// successorParent.setLeftNode(thisNode);
// successorParent.setRightNode(new Node<>(successorParent, NodeType.PAR));
// successorParent = successorParent.getRightNode();
// }
// }
// return forkNode;
// } else {
// return new Node<>(parent, currentFork);
// }
// }
public static boolean checkNFJProperty(DirectedAcyclicGraph<Job, DefaultEdge> jobDag) { public static boolean checkNFJProperty(DirectedAcyclicGraph<Job, DefaultEdge> jobDag) {
LinkedList<Job> joinNodes = new LinkedList<>(); LinkedList<Job> joinNodes = new LinkedList<>();
...@@ -327,43 +410,58 @@ public class DagTask implements Task { ...@@ -327,43 +410,58 @@ public class DagTask implements Task {
return true; return true;
} }
for (Job j : joinNodes) { nextJoin: for (Job j : joinNodes) {
nextFork: for (Job f : forkNodes) { for (Job f : forkNodes) {
Set<DefaultEdge> edgeSet = jobDag.getAllEdges(f, j); Set<Job> ancestorsOfJ = jobDag.getAncestors(jobDag, j);
Set<Job> ancesotorsOfF = jobDag.getAncestors(jobDag, f);
for (DefaultEdge e : edgeSet) { ancesotorsOfF.add(f);
Job a = jobDag.getEdgeSource(e); Set<Job> inbetweenJobs = new HashSet<>(ancestorsOfJ);
if (a != f) { inbetweenJobs.removeAll(ancesotorsOfF);
Set<Job> succAndPred = new HashSet<>(); if (inbetweenJobs.isEmpty()) {
succAndPred.addAll(Graphs.predecessorListOf(jobDag, a)); continue;
succAndPred.addAll(Graphs.successorListOf(jobDag, a));
for (Job b : succAndPred) {
if (!((jobDag.getAncestors(jobDag, j).contains(b) || b == j)
&& (jobDag.getDescendants(jobDag, f).contains(b)
|| b == f))) {
continue nextFork;
}
} }
for (Job a : inbetweenJobs) {
List<Job> neighboursOfA = Graphs.neighborListOf(jobDag, a);
for (Job b : neighboursOfA) {
if ((jobDag.getAncestors(jobDag, j).contains(b) || b == j)
&& (jobDag.getDescendants(jobDag, f).contains(b) || b == f)) {
continue nextJoin;
} }
} }
return true;
} }
} }
return false; return false;
} }
return true;
}
private static Job findJoin(DirectedAcyclicGraph<Job, DefaultEdge> jobDag, Job forkNode, private static Job findJoin(DirectedAcyclicGraph<Job, DefaultEdge> jobDag, Job forkNode,
Job sink, List<Job> joinNodes) { Job sink, List<Job> joinNodes) {
for (Job j : joinNodes) { for (Job j : joinNodes) {
final Set<DefaultEdge> edgeSet = new HashSet<>(); Set<Job> ancestorsOfJ = jobDag.getAncestors(jobDag, j);
AllDirectedPaths<Job, DefaultEdge> finder = new AllDirectedPaths<>(jobDag); Set<Job> ancesotorsOfFork = jobDag.getAncestors(jobDag, forkNode);
finder.getAllPaths(forkNode, j, true, null) ancesotorsOfFork.add(forkNode);
.forEach(g -> edgeSet.addAll(g.getEdgeList())); Set<Job> inbetweenJobs = new HashSet<>(ancestorsOfJ);
Set<DefaultEdge> outgoingEdges = jobDag.outgoingEdgesOf(forkNode); inbetweenJobs.removeAll(ancesotorsOfFork);
if (edgeSet.containsAll(outgoingEdges)) { if (inbetweenJobs.isEmpty()) {
continue;
}
List<Job> successorOfFork = Graphs.successorListOf(jobDag, forkNode);
if (inbetweenJobs.containsAll(successorOfFork)) {
return j; return j;
} }
// final Set<DefaultEdge> edgeSet = new HashSet<>();
// AllDirectedPaths<Job, DefaultEdge> finder = new AllDirectedPaths<>(jobDag);
// finder.getAllPaths(forkNode, j, true, null)
// .forEach(g -> edgeSet.addAll(g.getEdgeList()));
// Set<DefaultEdge> outgoingEdges = jobDag.outgoingEdgesOf(forkNode);
// if (edgeSet.containsAll(outgoingEdges)) {
// return j;
// }
} }
return sink; return sink;
...@@ -394,63 +492,57 @@ public class DagTask implements Task { ...@@ -394,63 +492,57 @@ public class DagTask implements Task {
DirectedAcyclicGraph<Job, DefaultEdge> jobDag = DirectedAcyclicGraph<Job, DefaultEdge> jobDag =
new DirectedAcyclicGraph<>(DefaultEdge.class); new DirectedAcyclicGraph<>(DefaultEdge.class);
traverseNodes(jobDag, tree.getRootNode(), TraversalOrder.LEFT); if (tree.getRootNode() != null) {
traverseNodes(jobDag, tree.getRootNode());
}
return jobDag; return jobDag;
} }
private static Set<Job> traverseNodes(DirectedAcyclicGraph<Job, DefaultEdge> jobDag, private static GraphEndPoints traverseNodes(DirectedAcyclicGraph<Job, DefaultEdge> jobDag,
Node<Job> node, TraversalOrder order) { Node<Job> node) {
switch (node.getNodeType()) { switch (node.getNodeType()) {
case LEAF: { case LEAF: {
Job j = node.getObject(); Job j = node.getObject();
jobDag.addVertex(j); jobDag.addVertex(j);
return new HashSet<Job>(Arrays.asList(j)); Set<Job> endPoints = new HashSet<Job>(Arrays.asList(j));
return new GraphEndPoints(endPoints, endPoints);
} }
case SEQ: { case SEQ: {
Set<Job> left = traverseNodes(jobDag, node.getLeftNode(), TraversalOrder.LEFT); GraphEndPoints leftEndPoints = traverseNodes(jobDag, node.getLeftNode());
Set<Job> right = GraphEndPoints rightEndPoints = traverseNodes(jobDag, node.getRightNode());
traverseNodes(jobDag, node.getRightNode(), TraversalOrder.RIGHT); for (Job l : leftEndPoints.right) {
for (Job r : rightEndPoints.left) {
if (r == l) {
continue;
}
try { try {
for (Job l : left) {
for (Job r : right) {
jobDag.addDagEdge(l, r); jobDag.addDagEdge(l, r);
}
}
} catch (Exception e) { } catch (Exception e) {
int test = 3;
} }
if (order == TraversalOrder.LEFT) {
Set<Job> openEndJobs = new HashSet<>();
for (Job j : right) {
if (jobDag.outDegreeOf(j) == 0) {
openEndJobs.add(j);
} }
for (Job d : jobDag.getDescendants(jobDag, j)) {
if (jobDag.outDegreeOf(d) == 0) {
openEndJobs.add(d);
} }
}
} return new GraphEndPoints(leftEndPoints.left, rightEndPoints.right);
return openEndJobs;
} else {
return left;
}
// return order == TraversalOrder.LEFT ? right : left; // left -> right; right
// ->
} }
case PAR: { case PAR: {
Set<Job> left = traverseNodes(jobDag, node.getLeftNode(), order); GraphEndPoints leftEndPoints = traverseNodes(jobDag, node.getLeftNode());
Set<Job> right = traverseNodes(jobDag, node.getRightNode(), order); GraphEndPoints rightEndPoints = traverseNodes(jobDag, node.getRightNode());
return Sets.newHashSet(Iterables.concat(left, right)); Set<Job> leftEndPointJobs = Sets
.newHashSet(Iterables.concat(leftEndPoints.left, rightEndPoints.left));
Set<Job> rightEndPointJobs = Sets.newHashSet(
Iterables.concat(leftEndPoints.right, rightEndPoints.right));
return new GraphEndPoints(leftEndPointJobs, rightEndPointJobs);
} }
default: default:
break; break;
} }
return new HashSet<>(); return new GraphEndPoints(new HashSet<>(), new HashSet<>());
} }
private class GraphEndPoints { public static class GraphEndPoints {
public Set<Job> left; public Set<Job> left;
public Set<Job> right; public Set<Job> right;
......
package mvd.jester.utils; package mvd.jester.utils;
public class BinaryDecompositionTree<N> { public class BinaryDecompositionTree<N> {
private Node<N> root; private Node<N> root;
...@@ -12,6 +13,9 @@ public class BinaryDecompositionTree<N> { ...@@ -12,6 +13,9 @@ public class BinaryDecompositionTree<N> {
} }
public boolean contains(N object) { public boolean contains(N object) {
if (root == null) {
return false;
}
return root.contains(object); return root.contains(object);
} }
...@@ -75,8 +79,9 @@ public class BinaryDecompositionTree<N> { ...@@ -75,8 +79,9 @@ public class BinaryDecompositionTree<N> {
/** /**
* @param leftNode the leftNode to set * @param leftNode the leftNode to set
*/ */
public void setLeftNode(Node<T> leftNode) { public Node<T> setLeftNode(Node<T> leftNode) {
this.leftNode = leftNode; this.leftNode = leftNode;
return this.leftNode;
} }
/** /**
...@@ -89,8 +94,9 @@ public class BinaryDecompositionTree<N> { ...@@ -89,8 +94,9 @@ public class BinaryDecompositionTree<N> {
/** /**
* @param rightNode the rightNode to set * @param rightNode the rightNode to set
*/ */
public void setRightNode(Node<T> rightNode) { public Node<T> setRightNode(Node<T> rightNode) {
this.rightNode = rightNode; this.rightNode = rightNode;
return this.rightNode;
} }
/** /**
......
package mvd.jester.model; package mvd.jester.model;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import org.jgrapht.Graphs;
import org.jgrapht.experimental.dag.DirectedAcyclicGraph; import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DefaultEdge;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
...@@ -12,20 +13,6 @@ import mvd.jester.utils.BinaryDecompositionTree; ...@@ -12,20 +13,6 @@ import mvd.jester.utils.BinaryDecompositionTree;
public class TestDagUtils { public class TestDagUtils {
@Test @Test
@DisplayName("Test if a NFJ Graph is created correctly.")
public void checkNFJCreation() {
for (int i = 0; i < 100; ++i) {
DagTaskBuilder builder = new DagTaskBuilder();
DirectedAcyclicGraph<Job, DefaultEdge> jobDag = builder.generateTask().getJobDag();
DirectedAcyclicGraph<Job, DefaultEdge> modifiedJobDag = DagUtils.createNFJGraph(jobDag);
assertTrue(DagUtils.checkNFJProperty(modifiedJobDag));
}
}
@Test
@DisplayName("Test if dynamic segments are constructed correctly.") @DisplayName("Test if dynamic segments are constructed correctly.")
public void checkPathLength() { public void checkPathLength() {
DirectedAcyclicGraph<Job, DefaultEdge> jobDag = createJobDag(); DirectedAcyclicGraph<Job, DefaultEdge> jobDag = createJobDag();
...@@ -45,15 +32,73 @@ public class TestDagUtils { ...@@ -45,15 +32,73 @@ public class TestDagUtils {
@Test @Test
@DisplayName("Check if NFJ DAGs are created correctly.")
void checkNfjDagCreation() {
for (int i = 0; i < 100; ++i) {
DagTaskBuilder b = new DagTaskBuilder();
DagTask t = b.generateTask();
DirectedAcyclicGraph<Job, DefaultEdge> nfjDag = DagUtils.createNFJGraph(t.getJobDag());
assertTrue(DagUtils.checkNFJProperty(nfjDag));
}
}
@Test
@DisplayName("Check if Decomposition Tree is created correctly for NFJ Dag.") @DisplayName("Check if Decomposition Tree is created correctly for NFJ Dag.")
void checkDecompositionTreeCreation() { void checkDecompositionTreeCreation() {
DirectedAcyclicGraph<Job, DefaultEdge> jobDag = createJobDag(); for (int i = 0; i < 100; ++i) {
DagTaskBuilder b = new DagTaskBuilder();
DagTask t = b.generateTask();
DirectedAcyclicGraph<Job, DefaultEdge> jobDag = t.getJobDag();
DirectedAcyclicGraph<Job, DefaultEdge> nfjJobDag = DagUtils.createNFJGraph(jobDag);
BinaryDecompositionTree<Job> tree = DagUtils.createDecompositionTree(jobDag); BinaryDecompositionTree<Job> tree = DagUtils.createDecompositionTree(nfjJobDag);
DirectedAcyclicGraph<Job, DefaultEdge> jobDagFromTree = DirectedAcyclicGraph<Job, DefaultEdge> jobDagFromTree =
DagUtils.createNFJfromDecompositionTree(tree); DagUtils.createNFJfromDecompositionTree(tree);
assertTrue(jobDag.equals(jobDagFromTree)); assertTrue(jobDag.vertexSet().equals(nfjJobDag.vertexSet()));
assertTrue(jobDag.vertexSet().equals(jobDagFromTree.vertexSet()));
assertTrue(jobDagFromTree.edgeSet().size() == nfjJobDag.edgeSet().size());
for (DefaultEdge e : nfjJobDag.edgeSet()) {
Job target = nfjJobDag.getEdgeTarget(e);
Job source = nfjJobDag.getEdgeSource(e);
assertTrue(jobDagFromTree.containsEdge(source, target));
}
for (Job j : nfjJobDag) {
for (Job n : nfjJobDag) {
if (n == j) {
continue;
}
if (nfjJobDag.containsEdge(n, j)) {
assertTrue(jobDagFromTree.containsEdge(n, j));
}
}
assertTrue(nfjJobDag.inDegreeOf(j) == jobDagFromTree.inDegreeOf(j));
assertTrue(nfjJobDag.outDegreeOf(j) == jobDagFromTree.outDegreeOf(j));
for (Job p : Graphs.predecessorListOf(nfjJobDag, j)) {
assertTrue(Graphs.predecessorListOf(jobDagFromTree, j).contains(p));
}
for (Job s : Graphs.successorListOf(jobDagFromTree, j)) {
assertTrue(Graphs.successorListOf(jobDagFromTree, j).contains(s));
}
for (Job a : nfjJobDag.getAncestors(nfjJobDag, j)) {
assertTrue(jobDagFromTree.getAncestors(jobDagFromTree, j).contains(a));
}
for (Job d : nfjJobDag.getDescendants(nfjJobDag, j)) {
assertTrue(jobDagFromTree.getDescendants(jobDagFromTree, j).contains(d));
}
}
}
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment