diff --git a/src/main/java/mvd/jester/model/DagTask.java b/src/main/java/mvd/jester/model/DagTask.java index c7fef52..893a791 100644 --- a/src/main/java/mvd/jester/model/DagTask.java +++ b/src/main/java/mvd/jester/model/DagTask.java @@ -2,15 +2,14 @@ package mvd.jester.model; import java.util.Arrays; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Set; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import org.jgrapht.Graphs; -import org.jgrapht.alg.shortestpath.AllDirectedPaths; import org.jgrapht.experimental.dag.DirectedAcyclicGraph; import org.jgrapht.graph.DefaultEdge; import mvd.jester.utils.BinaryDecompositionTree; @@ -241,65 +240,67 @@ public class DagTask implements Task { public static BinaryDecompositionTree createDecompositionTree( DirectedAcyclicGraph jobDag, LinkedList forkNodes, LinkedList joinNodes) { - Job source = forkNodes.getFirst(); - Job sink = joinNodes.getLast(); - Job current = source; - BinaryDecompositionTree tree = new BinaryDecompositionTree<>(); - DagUtils.constructTree(jobDag, null, current, tree, forkNodes, joinNodes, sink); + Optional source = Optional.empty(); + Optional 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(); + + DagUtils.treeDec(jobDag, null, current, tree, forkNodes, joinNodes, sink.get()); + } return tree; } - private static Node constructTree(DirectedAcyclicGraph jobDag, - Node parent, Job current, BinaryDecompositionTree tree, - LinkedList forkVertices, LinkedList 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!"); - } + private static Node treeDec(DirectedAcyclicGraph jobDag, + Node parentNode, Job currentJob, BinaryDecompositionTree tree, + LinkedList forkVertices, LinkedList joinVertices, Job sinkJob) { + if (forkVertices.contains(currentJob)) { + Job forkJob = currentJob; + Job joinJob = findJoin(jobDag, forkJob, sinkJob, joinVertices); - Node forkNode = new Node(parent, NodeType.SEQ); + Node seqForkNode = new Node(parentNode, NodeType.SEQ); + seqForkNode.setLeftNode(new Node(seqForkNode, forkJob)); if (tree.isEmpty()) { - tree.setRoot(forkNode); + tree.setRoot(seqForkNode); } - forkNode.setLeftNode(new Node(forkNode, current)); - final Node continuationNode; - if (!tree.contains(join)) { - forkNode.setRightNode(new Node(forkNode, NodeType.SEQ)); - Node joinNode = forkNode.getRightNode(); - Iterator 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 successorNode = joinNode.getRightNode(); - successorNode.setLeftNode(new Node<>(successorNode, join)); - Node 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(); + final Node parContinuationNode; + if (!tree.contains(joinJob)) { + Node seqJoinNode = + seqForkNode.setRightNode(new Node(seqForkNode, NodeType.SEQ)); + + Node subTreeOfJoin = treeDec(jobDag, seqJoinNode, joinJob, tree, + forkVertices, joinVertices, sinkJob); + seqJoinNode.setRightNode(subTreeOfJoin); + + parContinuationNode = + seqJoinNode.setLeftNode(new Node(seqJoinNode, NodeType.PAR)); } else { - forkNode.setRightNode(new Node<>(forkNode, NodeType.PAR)); - continuationNode = forkNode.getRightNode(); + parContinuationNode = + seqForkNode.setRightNode(new Node(seqForkNode, NodeType.PAR)); } - Node successorParent = continuationNode; - List successors = Graphs.successorListOf(jobDag, current); + + Node successorParent = parContinuationNode; + List successors = Graphs.successorListOf(jobDag, currentJob); // create leftSide of joinNode for (int i = 0; i < successors.size(); ++i) { Job succ = successors.get(i); - Node thisNode = constructTree(jobDag, successorParent, succ, tree, - forkVertices, joinVertices, sink); + Node thisNode = treeDec(jobDag, successorParent, succ, tree, forkVertices, + joinVertices, sinkJob); if (i == successors.size() - 1) { successorParent.setRightNode(thisNode); } else if (i == successors.size() - 2) { @@ -311,12 +312,94 @@ public class DagTask implements Task { } } - return forkNode; + return seqForkNode; } else { - return new Node<>(parent, current); + List successorList = Graphs.successorListOf(jobDag, currentJob); + if (successorList.size() > 0) { + Job successor = successorList.get(0); + if (!tree.contains(successor)) { + Node seqJobNode = new Node(parentNode, NodeType.SEQ); + if (tree.isEmpty()) { + tree.setRoot(seqJobNode); + } + seqJobNode.setLeftNode(new Node(seqJobNode, currentJob)); + Node contNode = treeDec(jobDag, seqJobNode, successor, tree, + forkVertices, joinVertices, sinkJob); + seqJobNode.setRightNode(contNode); + return seqJobNode; + } + } + Node jobNode = new Node(parentNode, currentJob); + if (tree.isEmpty()) { + tree.setRoot(jobNode); + } + return jobNode; } } + // private static Node constructTree(DirectedAcyclicGraph jobDag, + // Node parent, Job currentFork, BinaryDecompositionTree tree, + // LinkedList forkVertices, LinkedList joinVertices, Job sink) { + // if (forkVertices.contains(currentFork)) { + // Job join = findJoin(jobDag, currentFork, sink, joinVertices); + + // Node forkNode = new Node(parent, NodeType.SEQ); + // if (tree.isEmpty()) { + // tree.setRoot(forkNode); + // } + // forkNode.setLeftNode(new Node(forkNode, currentFork)); + // final Node continuationNode; + // if (!tree.contains(join)) { + // forkNode.setRightNode(new Node(forkNode, NodeType.SEQ)); + // Node joinNode = forkNode.getRightNode(); + // Iterator 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 successorNode = joinNode.getRightNode(); + // successorNode.setLeftNode(new Node<>(successorNode, join)); + // Node 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 successorParent = continuationNode; + // List successors = Graphs.successorListOf(jobDag, currentFork); + // // create leftSide of joinNode + // for (int i = 0; i < successors.size(); ++i) { + // Job succ = successors.get(i); + // Node 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 jobDag) { LinkedList joinNodes = new LinkedList<>(); LinkedList forkNodes = new LinkedList<>(); @@ -327,43 +410,58 @@ public class DagTask implements Task { return true; } - for (Job j : joinNodes) { - nextFork: for (Job f : forkNodes) { - Set edgeSet = jobDag.getAllEdges(f, j); - - for (DefaultEdge e : edgeSet) { - Job a = jobDag.getEdgeSource(e); - if (a != f) { - Set succAndPred = new HashSet<>(); - succAndPred.addAll(Graphs.predecessorListOf(jobDag, a)); - 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; - } + nextJoin: for (Job j : joinNodes) { + for (Job f : forkNodes) { + Set ancestorsOfJ = jobDag.getAncestors(jobDag, j); + Set ancesotorsOfF = jobDag.getAncestors(jobDag, f); + ancesotorsOfF.add(f); + Set inbetweenJobs = new HashSet<>(ancestorsOfJ); + inbetweenJobs.removeAll(ancesotorsOfF); + if (inbetweenJobs.isEmpty()) { + continue; + } + + for (Job a : inbetweenJobs) { + List 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 jobDag, Job forkNode, Job sink, List joinNodes) { for (Job j : joinNodes) { - final Set edgeSet = new HashSet<>(); - AllDirectedPaths finder = new AllDirectedPaths<>(jobDag); - finder.getAllPaths(forkNode, j, true, null) - .forEach(g -> edgeSet.addAll(g.getEdgeList())); - Set outgoingEdges = jobDag.outgoingEdgesOf(forkNode); - if (edgeSet.containsAll(outgoingEdges)) { + Set ancestorsOfJ = jobDag.getAncestors(jobDag, j); + Set ancesotorsOfFork = jobDag.getAncestors(jobDag, forkNode); + ancesotorsOfFork.add(forkNode); + Set inbetweenJobs = new HashSet<>(ancestorsOfJ); + inbetweenJobs.removeAll(ancesotorsOfFork); + if (inbetweenJobs.isEmpty()) { + continue; + } + + List successorOfFork = Graphs.successorListOf(jobDag, forkNode); + + if (inbetweenJobs.containsAll(successorOfFork)) { return j; } + + // final Set edgeSet = new HashSet<>(); + // AllDirectedPaths finder = new AllDirectedPaths<>(jobDag); + // finder.getAllPaths(forkNode, j, true, null) + // .forEach(g -> edgeSet.addAll(g.getEdgeList())); + // Set outgoingEdges = jobDag.outgoingEdgesOf(forkNode); + // if (edgeSet.containsAll(outgoingEdges)) { + // return j; + // } } return sink; @@ -394,63 +492,57 @@ public class DagTask implements Task { DirectedAcyclicGraph jobDag = new DirectedAcyclicGraph<>(DefaultEdge.class); - traverseNodes(jobDag, tree.getRootNode(), TraversalOrder.LEFT); + if (tree.getRootNode() != null) { + traverseNodes(jobDag, tree.getRootNode()); + } return jobDag; } - private static Set traverseNodes(DirectedAcyclicGraph jobDag, - Node node, TraversalOrder order) { + private static GraphEndPoints traverseNodes(DirectedAcyclicGraph jobDag, + Node node) { switch (node.getNodeType()) { case LEAF: { Job j = node.getObject(); jobDag.addVertex(j); - return new HashSet(Arrays.asList(j)); + Set endPoints = new HashSet(Arrays.asList(j)); + return new GraphEndPoints(endPoints, endPoints); } case SEQ: { - Set left = traverseNodes(jobDag, node.getLeftNode(), TraversalOrder.LEFT); - Set right = - traverseNodes(jobDag, node.getRightNode(), TraversalOrder.RIGHT); - try { - for (Job l : left) { - for (Job r : right) { - jobDag.addDagEdge(l, r); - } - } - } catch (Exception e) { - } - if (order == TraversalOrder.LEFT) { - Set openEndJobs = new HashSet<>(); - for (Job j : right) { - if (jobDag.outDegreeOf(j) == 0) { - openEndJobs.add(j); + GraphEndPoints leftEndPoints = traverseNodes(jobDag, node.getLeftNode()); + GraphEndPoints rightEndPoints = traverseNodes(jobDag, node.getRightNode()); + for (Job l : leftEndPoints.right) { + for (Job r : rightEndPoints.left) { + if (r == l) { + continue; } - for (Job d : jobDag.getDescendants(jobDag, j)) { - if (jobDag.outDegreeOf(d) == 0) { - openEndJobs.add(d); - } + try { + jobDag.addDagEdge(l, r); + } catch (Exception e) { + int test = 3; } } - return openEndJobs; - } else { - return left; } - // return order == TraversalOrder.LEFT ? right : left; // left -> right; right - // -> + + return new GraphEndPoints(leftEndPoints.left, rightEndPoints.right); } case PAR: { - Set left = traverseNodes(jobDag, node.getLeftNode(), order); - Set right = traverseNodes(jobDag, node.getRightNode(), order); - return Sets.newHashSet(Iterables.concat(left, right)); + GraphEndPoints leftEndPoints = traverseNodes(jobDag, node.getLeftNode()); + GraphEndPoints rightEndPoints = traverseNodes(jobDag, node.getRightNode()); + Set leftEndPointJobs = Sets + .newHashSet(Iterables.concat(leftEndPoints.left, rightEndPoints.left)); + Set rightEndPointJobs = Sets.newHashSet( + Iterables.concat(leftEndPoints.right, rightEndPoints.right)); + return new GraphEndPoints(leftEndPointJobs, rightEndPointJobs); } default: break; } - return new HashSet<>(); + return new GraphEndPoints(new HashSet<>(), new HashSet<>()); } - private class GraphEndPoints { + public static class GraphEndPoints { public Set left; public Set right; diff --git a/src/main/java/mvd/jester/utils/BinaryDecompositionTree.java b/src/main/java/mvd/jester/utils/BinaryDecompositionTree.java index dd2f4e9..96d6191 100644 --- a/src/main/java/mvd/jester/utils/BinaryDecompositionTree.java +++ b/src/main/java/mvd/jester/utils/BinaryDecompositionTree.java @@ -1,5 +1,6 @@ package mvd.jester.utils; + public class BinaryDecompositionTree { private Node root; @@ -12,6 +13,9 @@ public class BinaryDecompositionTree { } public boolean contains(N object) { + if (root == null) { + return false; + } return root.contains(object); } @@ -75,8 +79,9 @@ public class BinaryDecompositionTree { /** * @param leftNode the leftNode to set */ - public void setLeftNode(Node leftNode) { + public Node setLeftNode(Node leftNode) { this.leftNode = leftNode; + return this.leftNode; } /** @@ -89,8 +94,9 @@ public class BinaryDecompositionTree { /** * @param rightNode the rightNode to set */ - public void setRightNode(Node rightNode) { + public Node setRightNode(Node rightNode) { this.rightNode = rightNode; + return this.rightNode; } /** diff --git a/src/test/java/mvd/jester/model/TestDagUtils.java b/src/test/java/mvd/jester/model/TestDagUtils.java index 53e9bed..be35871 100644 --- a/src/test/java/mvd/jester/model/TestDagUtils.java +++ b/src/test/java/mvd/jester/model/TestDagUtils.java @@ -1,6 +1,7 @@ package mvd.jester.model; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.jgrapht.Graphs; import org.jgrapht.experimental.dag.DirectedAcyclicGraph; import org.jgrapht.graph.DefaultEdge; import org.junit.jupiter.api.DisplayName; @@ -12,20 +13,6 @@ import mvd.jester.utils.BinaryDecompositionTree; public class TestDagUtils { @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 jobDag = builder.generateTask().getJobDag(); - - DirectedAcyclicGraph modifiedJobDag = DagUtils.createNFJGraph(jobDag); - - assertTrue(DagUtils.checkNFJProperty(modifiedJobDag)); - } - } - - - @Test @DisplayName("Test if dynamic segments are constructed correctly.") public void checkPathLength() { DirectedAcyclicGraph jobDag = createJobDag(); @@ -45,15 +32,73 @@ public class TestDagUtils { @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 nfjDag = DagUtils.createNFJGraph(t.getJobDag()); + + assertTrue(DagUtils.checkNFJProperty(nfjDag)); + } + } + + @Test @DisplayName("Check if Decomposition Tree is created correctly for NFJ Dag.") void checkDecompositionTreeCreation() { - DirectedAcyclicGraph jobDag = createJobDag(); - - BinaryDecompositionTree tree = DagUtils.createDecompositionTree(jobDag); - DirectedAcyclicGraph jobDagFromTree = - DagUtils.createNFJfromDecompositionTree(tree); + for (int i = 0; i < 100; ++i) { + DagTaskBuilder b = new DagTaskBuilder(); + DagTask t = b.generateTask(); + DirectedAcyclicGraph jobDag = t.getJobDag(); + DirectedAcyclicGraph nfjJobDag = DagUtils.createNFJGraph(jobDag); + + BinaryDecompositionTree tree = DagUtils.createDecompositionTree(nfjJobDag); + DirectedAcyclicGraph jobDagFromTree = + DagUtils.createNFJfromDecompositionTree(tree); + + 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)); + } + } - assertTrue(jobDag.equals(jobDagFromTree)); + } }