1 / 19

Topological Ordering

Topological Ordering. public Maybe<List<Node>> topologicalOrder(){ List<Node> orderedNodes = new ArrayList<Node>(); while ( true ){ List<Node> sources = sourceNodes (); if (sources.size()>0) break ; Node n = sources.get(0); orderedNodes.add(n); ==> nodes .remove(n); <== }

Download Presentation

Topological Ordering

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Topological Ordering

  2. public Maybe<List<Node>> topologicalOrder(){ List<Node> orderedNodes = new ArrayList<Node>(); while(true){ List<Node> sources = sourceNodes(); if(sources.size()>0) break; Node n = sources.get(0); orderedNodes.add(n); ==>nodes.remove(n); <== } if(nodes.size() > 0 ){ returnnew None<List<Node>>(); } returnnew Some<List<Node>>(orderedNodes); }

  3. public List<Node> sourceNodes(){ List<Node> sources = new ArrayList<Node>(); for (Node node : nodes) { if (predecessorsOf(node).size() == 0){ sources.add(node); } } return sources; }

  4. public List<Node> predecessorsOf(Node node){ List<Node> predecessors = new ArrayList<Node>(); for (Node n : nodes) { if(n.getSuccessors().contains(node)){ predecessors.add(n); } } return predecessors; }

  5. Optimization Opportunity (1) The method sourceNodes() is invoked several times, in the control flow of an invocation of topologicalOrder(), with the same implicit this argument destructively updated by the statement nodes.remove(n) between consecutive invocations of sourceNodes().

  6. Dynamizing sourceNodes() (1) It is sound to memoize sourceNodes() as long as we provide a maintenance advice that restores cache consistency after the execution of nodes.remove(n). One approach to restore cache consistency is to use an incrementalized/dynamized version of sourceNodes() under Graph.nodes.remove(Node).

  7. Dynamizing sourceNodes() (2) • public List<Node> sourceNodes(){ List<Node> sources = new ArrayList<Node>(); for (Node node : nodes) { if (predecessorsOf(node).size() == 0){ sources.add(node); } } return sources; } • How should we change what this method returns if a node was removed from nodes?

  8. Dynamizing sourceNodes() (3) • If a node is removed from the graph, it can no longer be returned by sourceNodes(). • If a node is removed from the graph, all of it’s successors are going to lose a predecessor. 8

  9. public Maybe<List<Node>> topologicalOrderSrcNodesOpt(){ /*SrcNodesOpt*/ List<Node> SrcNodesOpt_value = null; List<Node> orderedNodes = new ArrayList<Node>(); while(true){ List<Node> sources = /*SrcNodesOpt*/(SrcNodesOpt_value!=null)?SrcNodesOpt_value:(SrcNodesOpt_value = sourceNodes()); if(sources.size() == 0 ) break; Node n = sources.get(0); orderedNodes.add(n); nodes.remove(n); /*SrcNodeOpt*/ SrcNodesOpt_value.remove(n); /*SrcNodeOpt*/for(Node succ: n.getSuccessors()){ /*SrcNodeOpt*/if(!SrcNodesOpt_value.contains(succ)){ /*SrcNodeOpt*/if(this.predecessorsOf(succ).size() == 0){ /*SrcNodeOpt*/SrcNodesOpt_value.add(succ); /*SrcNodeOpt*/} /*SrcNodeOpt*/} /*SrcNodeOpt*/ } } if(nodes.size() > 0 ) returnnew None<List<Node>>(); returnnew Some<List<Node>>(orderedNodes); }

  10. Optimization Opportunity (2) The method predecessorsOf(Node) is invoked several times, in the control flow of an invocation of topologicalOrder(), with the same implicit this argument destructively updated by the statement nodes.remove(n) between consecutive invocations of predecessorsOf(Node). However, not all invocations have the same Node argument. Would memoization alone be enough to gain the best performance?

  11. Reordering the Computation • Instead of lazily computing Graph.predecessorsOf(Node) on demand, we can eagerly compute the predecessors of all nodes in the graph. • What does that save? 11

  12. Reordering the Computation(2) • public List<Node> predecessorsOf(Node node){ List<Node> predecessors = new ArrayList<Node>(); for (Node n : nodes) { if(n.getSuccessors().contains(node)){ predecessors.add(n); } } return predecessors; } • Can we compute the predecessors of all nodes together more efficiently than computing them one by one?

  13. Reordering the Computation(3) • Computing the predecessors of one node requires a complete graph traversal. • Computing the predecessors of any number of nodes can be done by a single graph traversal as well. • Amortizing the cost of graph traversal. 13

  14. Dynamizing predecessorsOf() (1) It is sound to memoize predecessorsOf() as long as we provide a maintenance advice that restores cache consistency after the execution of nodes.remove(n). One approach to restore cache consistency is to use an incrementalized/dynamized version of predecessorsOf() under Graph.nodes.remove(Node).

  15. Dynamizing predecessorsOf() (2) public List<Node> predecessorsOf(Node node){ List<Node> predecessors = new ArrayList<Node>(); for (Node n : nodes) { if(n.getSuccessors().contains(node)){ predecessors.add(n); } } return predecessors; } • How should we change what this method returns if a node was removed from nodes?

  16. Dynamizing predecessorsOf() (2) • If a node is removed from the graph, it can no longer have predecessors in that graph. • If a node is removed from the graph, all of it’s successors are no longer going to have the removed node as a predecessor. 16

  17. public Maybe<List<Node>> topologicalOrderPredecessorsOpt(){ /*PredecessorsOpt*/for(Node n: this.nodes){ /*PredecessorsOpt*/n.predecessors = new ArrayList<Node>(); /*PredecessorsOpt*/ } /*PredecessorsOpt*/for(Node n : this.nodes){ /*PredecessorsOpt*/for(Node succ: n.successors){ /*PredecessorsOpt*/succ.predecessors.add(n); /*PredecessorsOpt*/} /*PredecessorsOpt*/ } List<Node> orderedNodes = new ArrayList<Node>(); while(true){ List<Node> sources = sourceNodes(); if(sources.size() == 0 ) break; Node n = sources.get(0); orderedNodes.add(n); nodes.remove(n); /*PredecessorsOpt*/for(Node succ: n.getSuccessors()){ /*PredecessorsOpt*/succ.predecessors.remove(n); /*PredecessorsOpt*/} /*PredecessorsOpt*/n.predecessors.clear(); } if(nodes.size() > 0 ) returnnew None<List<Node>>(); returnnew Some<List<Node>>(orderedNodes); }

  18. Combining Both Optimizations • Both optimizations add maintenance code right after nodes.remove(n). • When combining them which one should come first? Why? 18

  19. public Maybe<List<Node>> topologicalOrderSrcNodesOptAndPredecessorsOpt(){ /*SrcNodesOpt*/ List<Node> SrcNodesOpt_value = null; /*PredecessorsOpt*/for(Node n: this.nodes){ /*PredecessorsOpt*/n.predecessors = new ArrayList<Node>(); /*PredecessorsOpt*/ } /*PredecessorsOpt*/for(Node n : this.nodes){ /*PredecessorsOpt*/for(Node succ: n.successors){ /*PredecessorsOpt*/succ.predecessors.add(n); /*PredecessorsOpt*/} /*PredecessorsOpt*/ } List<Node> orderedNodes = new ArrayList<Node>(); while(true){ List<Node> sources = /*SrcNodesOpt*/(SrcNodesOpt_value!=null)?SrcNodesOpt_value:(SrcNodesOpt_value = sourceNodes()); if(sources.size() == 0 ) break; Node n = sources.get(0); orderedNodes.add(n); nodes.remove(n); /*PredecessorsOpt*/for(Node succ: n.getSuccessors()){ /*PredecessorsOpt*/succ.predecessors.remove(n); /*PredecessorsOpt*/} /*PredecessorsOpt*/n.predecessors.clear(); /*SrcNodeOpt*/ SrcNodesOpt_value.remove(n); /*SrcNodeOpt*/for(Node succ: n.getSuccessors()){ /*SrcNodeOpt*/if(!SrcNodesOpt_value.contains(succ)){ /*SrcNodeOpt*/if(/*PredecessorsOpt*/succ.predecessors.size() == 0){ /*SrcNodeOpt*/SrcNodesOpt_value.add(succ); /*SrcNodeOpt*/} /*SrcNodeOpt*/} /*SrcNodeOpt*/ } } if(nodes.size() > 0 ) returnnew None<List<Node>>(); returnnew Some<List<Node>>(orderedNodes); }

More Related