Adding sypet to repo
[Benchmarks_CSolver.git] / sypet / src / edu / utexas / sypet / synthesis / model / ReachabilityGraph.java
diff --git a/sypet/src/edu/utexas/sypet/synthesis/model/ReachabilityGraph.java b/sypet/src/edu/utexas/sypet/synthesis/model/ReachabilityGraph.java
new file mode 100644 (file)
index 0000000..416ca19
--- /dev/null
@@ -0,0 +1,664 @@
+/*
+ * Copyright (C) 2017 The SyPet Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package edu.utexas.sypet.synthesis.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import edu.utexas.sypet.util.TimeUtil;
+import uniol.apt.adt.StructuralExtensionRemover;
+import uniol.apt.adt.exception.ArcExistsException;
+import uniol.apt.adt.exception.StructureException;
+import uniol.apt.adt.pn.Flow;
+import uniol.apt.adt.pn.Marking;
+import uniol.apt.adt.pn.Node;
+import uniol.apt.adt.pn.PetriNet;
+import uniol.apt.adt.pn.Transition;
+import uniol.apt.adt.ts.Arc;
+import uniol.apt.adt.ts.State;
+import uniol.apt.adt.ts.TransitionSystem;
+import uniol.apt.analysis.exception.UnboundedException;
+
+/**
+ * This class represents a k-bounded reachability graph of a Petri net. Let's
+ * first define the reachability graph: The reachable markings of a Petri net
+ * form a graph. The arcs between markings each belong to a transition which is
+ * fired for getting from one transition to another. This graph may be
+ * infinitely large.
+ *
+ * @author Yu Feng
+ */
+public class ReachabilityGraph {
+
+       // The Petri net that we are handling
+       private final PetriNet pn;
+       // Map from visited markings to the corresponding nodes
+       private final Map<Marking, ReachabilityGraphNode> states = new HashMap<>();
+       // List of nodes which were generated but whose enabled transitions weren't
+       // handled yet.
+       private final Deque<ReachabilityGraphNode> unvisited = new LinkedList<>();
+       // List of nodes that were already visited, this is a list to implement
+       // iterators.
+       private final List<ReachabilityGraphNode> nodes = new ArrayList<>();
+       // Are we generating a coverability or a reachability graph?
+       private final boolean reachabilityGraph;
+       // k-bounded reachability graph.
+       private final int K = 3;
+
+       /**
+        * Construct the coverability graph for a given Petri net. If a coverability
+        * graph for this Petri net is already known, that instance is re-used
+        * instead of creating a new one.
+        * 
+        * @param pn
+        *            The Petri net whose coverability graph is wanted.
+        * @return A coverability graph.
+        */
+       static public ReachabilityGraph get(PetriNet pn) {
+               return get(pn, false);
+       }
+
+       /**
+        * Construct the reachability graph for a given Petri net. If a reachability
+        * graph for this Petri net is already known, that instance is re-used
+        * instead of creating a new one. Keep in mind that the reachability graph
+        * of a Petri net can be infinite!
+        * 
+        * @param pn
+        *            The Petri net whose coverability graph is wanted.
+        * @return A coverability graph.
+        */
+       static public ReachabilityGraph getReachabilityGraph(PetriNet pn) {
+               return get(pn, true);
+       }
+
+       /**
+        * Construct the coverability graph for a given Petri net. If a coverability
+        * graph for this Petri net is already known, that instance is re-used
+        * instead of creating a new one.
+        * 
+        * @param pn
+        *            The Petri net whose coverability graph is wanted.
+        * @param reachabilityGraph
+        *            Should just reachability be checked and coverability be
+        *            ignored?
+        * @return A coverability graph.
+        */
+       static private ReachabilityGraph get(PetriNet pn, boolean reachabilityGraph) {
+               String key = ReachabilityGraph.class.getName();
+               if (reachabilityGraph)
+                       key = key + "-reachability";
+
+               Object extension = null;
+               try {
+                       extension = pn.getExtension(key);
+               } catch (StructureException e) {
+                       // No such extension. Returning "null" would be too easy...
+               }
+
+               if (extension != null && extension instanceof ReachabilityGraph)
+                       return (ReachabilityGraph) extension;
+
+               ReachabilityGraph result = new ReachabilityGraph(pn, reachabilityGraph);
+               // Save this coverability graph as an extension, but make sure that it
+               // is removed if the structure of
+               // the Petri net is changed in any way.
+               pn.putExtension(key, result);
+               pn.addListener(new StructuralExtensionRemover<PetriNet, Flow, Node>(key));
+               return result;
+       }
+
+       /**
+        * Construct the coverability graph for a given Petri net. This constructor
+        * is actually cheap. The coverability graph is constructed on-demand when
+        * needed. If you want to force full calculation of the graph, use the
+        * {@link #calculateNodes() calculateNodes} method.
+        * 
+        * @param pn
+        *            The Petri net whose coverability graph is wanted.
+        * @param reachabilityGraph
+        *            Should just reachability be checked and coverability be
+        *            ignored?
+        */
+       private ReachabilityGraph(PetriNet pn, boolean reachabilityGraph) {
+               this.pn = pn;
+               this.reachabilityGraph = reachabilityGraph;
+               addNode(null, pn.getInitialMarkingCopy(), null, null);
+       }
+
+       /**
+        * Calculate all nodes of the coverability graph.
+        * 
+        * @return Number of nodes in the graph.
+        */
+       public int calculateNodes() {
+               while (true) {
+                       if (!visitNode())
+                               return nodes.size();
+               }
+       }
+
+       private boolean visitNode() {
+               // Pick a random, unvisited node
+               // (Here: breadth-first search so that we have short paths to the
+               // initial node in checkCover())
+               ReachabilityGraphNode node = unvisited.pollFirst();
+               if (node == null)
+                       return false;
+
+               // Make the node generate its postset
+               node.getPostsetEdges();
+               return true;
+       }
+
+       /**
+        * Generate the postset of a given node. This may only be called by
+        * CoverabilityGraphNode.
+        * 
+        * @param node
+        *            Node whose postset should get generated.
+        * @return The node's postset
+        */
+       Set<ReachabilityGraphEdge> getPostsetEdges(ReachabilityGraphNode node) {
+               // Now follow all activated transitions of that node
+               final Marking marking = node.getMarking();
+               // bounded construction by K limit.
+               if (!bounded(marking))
+                       return new HashSet<>();
+
+               final Set<ReachabilityGraphEdge> result = new HashSet<>();
+               for (Transition t : pn.getTransitions()) {
+                       if (!t.isFireable(marking)) {
+                               continue;
+                       }
+
+                       Marking newMarking = t.fire(marking);
+                       // checkCover() will also change the marking of the Petri net if
+                       // some OMEGAs are created!
+                       ReachabilityGraphNode covered = checkCover(newMarking, node);
+                       result.add(addArc(t, newMarking, node, covered));
+               }
+
+               return result;
+       }
+
+       /**
+        * Check if the given marking covers any markings on the current path. If
+        * the marking covers some other marking, suitable omegas are inserted.
+        * 
+        * @param cur
+        *            The marking to check.
+        * @param parent
+        *            The immediate parent node.
+        * @return null if no covering occurred, else the node that is covered.
+        */
+       private ReachabilityGraphNode checkCover(Marking cur, ReachabilityGraphNode parent) {
+               if (reachabilityGraph)
+                       return null;
+               assert parent != null;
+               while (parent != null) {
+                       Marking m = parent.getMarking();
+                       if (cur.covers(m)) {
+                               return parent;
+                       }
+                       parent = parent.getParent();
+               }
+               return null;
+       }
+
+       /**
+        * Add a new arc to the LTS. The arc starts in the marking given at the top
+        * of the current path.
+        * 
+        * @param transition
+        *            The transition for the arc.
+        * @param cur
+        *            The marking to which the arc goes to.
+        * @param from
+        *            The marking from which the arc originates.
+        * @param covered
+        *            node whose marking is covered by the given marking (or null if
+        *            none)
+        */
+       private ReachabilityGraphEdge addArc(Transition transition, Marking cur, ReachabilityGraphNode from,
+                       ReachabilityGraphNode covered) {
+               ReachabilityGraphNode state = states.get(cur);
+               if (state == null) {
+                       state = addNode(transition, cur, from, covered);
+               }
+
+               return new ReachabilityGraphEdge(transition, from, state);
+       }
+
+       /**
+        * Add a node for the given marking to the LTS. Precondition: There is no
+        * node for that marking yet.
+        * 
+        * @param mark
+        *            The marking for which a node should be created.
+        * @param parent
+        *            The parent of this marking. Used for tracing the path to the
+        *            root.
+        * @param covered
+        *            node whose marking is covered by the given marking (or null if
+        *            none)
+        * @return the new node.
+        */
+       private ReachabilityGraphNode addNode(Transition transition, Marking mark, ReachabilityGraphNode parent,
+                       ReachabilityGraphNode covered) {
+               assert states.get(mark) == null;
+
+               // Copy the marking to make sure no one else messes with it.
+               mark = new Marking(mark);
+               ReachabilityGraphNode node = new ReachabilityGraphNode(this, transition, mark, parent, covered);
+               states.put(mark, node);
+               nodes.add(node);
+               // Append it to the tail of the unvisited nodes so that we do a
+               // breadth-first search
+               unvisited.addLast(node);
+               return node;
+       }
+
+       /**
+        * Get the initial node of this coverability graph.
+        * 
+        * @return the inital node.
+        */
+       public ReachabilityGraphNode getInitialNode() {
+               return nodes.get(0);
+       }
+
+       /**
+        * Return an iterable for all nodes in this coverability graph.
+        * 
+        * @return an iterable
+        */
+       public Iterable<ReachabilityGraphNode> getNodes() {
+               return new Iterable<ReachabilityGraphNode>() {
+
+                       @Override
+                       public Iterator<ReachabilityGraphNode> iterator() {
+                               return new Iterator<ReachabilityGraphNode>() {
+
+                                       private int position = 0;
+
+                                       @Override
+                                       public boolean hasNext() {
+                                               do {
+                                                       // Are we at the end yet?
+                                                       if (position < nodes.size()) {
+                                                               return true;
+                                                       }
+
+                                                       // Then try generating new nodes and try again
+                                               } while (visitNode());
+
+                                               return false;
+                                       }
+
+                                       @Override
+                                       public ReachabilityGraphNode next() {
+                                               // Make sure the next state is generated
+                                               hasNext();
+                                               return nodes.get(position++);
+                                       }
+
+                                       @Override
+                                       public void remove() {
+                                               throw new UnsupportedOperationException();
+                                       }
+                               };
+                       }
+               };
+       }
+
+       /**
+        * Return an iterable for all edges in this coverability graph.
+        * 
+        * @return an iterable
+        */
+       public Iterable<ReachabilityGraphEdge> getEdges() {
+               final ReachabilityGraph graph = this;
+               return new Iterable<ReachabilityGraphEdge>() {
+
+                       @Override
+                       public Iterator<ReachabilityGraphEdge> iterator() {
+                               return new ReachabilityGraphEdgesIterator(graph);
+                       }
+               };
+       }
+
+       /**
+        * Turn this coverability graph into a labeled transition system.
+        * 
+        * @throws UnboundedException
+        *             This exception is thrown when the Petri net is unbounded.
+        * @return The new transition system.
+        * @see #toCoverabilityLTS() For a version of this which does not reject
+        *      unbounded nets.
+        */
+       public TransitionSystem toReachabilityLTS() throws UnboundedException {
+               return toLTS(true);
+       }
+
+       /**
+        * Turn this coverability graph into a labeled transition system.
+        * 
+        * @return The new transition system.
+        * @see #toReachabilityLTS() For a version of this which rejects unbounded
+        *      nets.
+        */
+       public TransitionSystem toCoverabilityLTS() {
+               try {
+                       // If this is a reachability graph, we can just use that code (this
+                       // gets the name of the result
+                       // right and skips the Omega-check in toLTS(); no UnboundedException
+                       // can occur since covering is
+                       // not checked).
+                       if (reachabilityGraph)
+                               return toReachabilityLTS();
+                       return toLTS(false);
+               } catch (UnboundedException e) {
+                       // This should never happen, because we used "false" as the
+                       // parameter!
+                       throw new RuntimeException(e);
+               }
+       }
+
+       /**
+        * Turn this coverability graph into a labeled transition system.
+        * 
+        * @return The new transition system.
+        */
+       private TransitionSystem toLTS(boolean onlyReachability) throws UnboundedException {
+               String name = (onlyReachability ? "Reachability" : "Coverability") + " graph of " + this.pn.getName();
+               Map<Marking, State> ltsStates = new HashMap<>();
+               TransitionSystem lts = new TransitionSystem(name);
+               lts.putExtension(PetriNet.class.getName(), this.pn);
+
+               for (ReachabilityGraphNode node : this.getNodes()) {
+                       Marking mark = node.getMarking();
+                       assert ltsStates.get(mark) == null;
+
+                       State n = lts.createState();
+                       ltsStates.put(mark, n);
+                       n.putExtension(Marking.class.getName(), mark);
+                       n.putExtension(ReachabilityGraphNode.class.getName(), node);
+
+                       if (onlyReachability && mark.hasOmega()) {
+                               throw new UnboundedException(this.pn);
+                       }
+               }
+
+               for (ReachabilityGraphNode sourceNode : this.getNodes()) {
+                       State source = ltsStates.get(sourceNode.getMarking());
+                       for (ReachabilityGraphEdge edge : sourceNode.getPostsetEdges()) {
+                               State target = ltsStates.get(edge.getTarget().getMarking());
+                               Transition transition = edge.getTransition();
+                               try {
+                                       Arc e = lts.createArc(source.getId(), target.getId(), transition.getLabel());
+                                       e.putExtension(Transition.class.getName(), transition);
+                                       e.putExtension(ReachabilityGraphEdge.class.getName(), edge);
+                               } catch (ArcExistsException e) {
+                               }
+                       }
+               }
+
+               // Set up the LTS' initial state
+               Marking initialMarking = pn.getInitialMarkingCopy();
+               State initialNode = ltsStates.get(initialMarking);
+               lts.setInitialState(initialNode);
+               assert initialNode != null;
+
+               return lts;
+       }
+
+       // the current path
+       private Stack<ReachabilityGraphNode> path = new Stack<>();
+       // the set of vertices on the path
+       private Set<ReachabilityGraphNode> onPath = new HashSet<>();
+
+       private List<Stack<ReachabilityGraphNode>> paths = new ArrayList<>();
+
+       public List<List<Set<Transition>>> allPaths(ReachabilityGraphNode s, ReachabilityGraphNode t) {
+               enumerate(s, t);
+               List<List<Set<Transition>>> sols = new ArrayList<>();
+               for (Stack<ReachabilityGraphNode> stack : paths) {
+                       List<Set<Transition>> list = new ArrayList<>();
+                       Stack<ReachabilityGraphNode> revStack = reverse(stack);
+                       ReachabilityGraphNode src = revStack.pop();
+                       while (!revStack.isEmpty()) {
+                               Set<Transition> set = new HashSet<>();
+                               ReachabilityGraphNode tgt = revStack.pop();
+                               Set<ReachabilityGraphEdge> edges = src.getPostsetEdgesByTgt(tgt);
+                               for (ReachabilityGraphEdge e : edges) {
+                                       if (e.getTransition().getId().contains("clone"))
+                                               continue;
+
+                                       set.add(e.getTransition());
+                               }
+
+                               src = tgt;
+                               if (set.size() > 0)
+                                       list.add(set);
+                       }
+                       sols.add(list);
+               }
+               return sols;
+       }
+       
+       public boolean isReachable(ReachabilityGraphNode s, ReachabilityGraphNode t) {
+               Set<ReachabilityGraphNode> visited = new HashSet<>();
+               Stack<ReachabilityGraphNode> worklist = new Stack<>();
+               worklist.push(s);
+               while (!worklist.isEmpty()) {
+                       ReachabilityGraphNode worker = worklist.pop();
+                       if (visited.contains(worker))
+                               continue;
+
+                       if(worker.equals(t)) return true;
+                       
+                       visited.add(worker);
+                       for (ReachabilityGraphNode succ : worker.getPostset()) {
+                               worklist.push(succ);
+                       }
+               }
+               return visited.contains(t);
+       }
+       
+       public List<Set<Transition>> shortestPath(ReachabilityGraphNode s, ReachabilityGraphNode t) {
+               Map<ReachabilityGraphNode, Integer> dist = new HashMap<>();
+               Map<ReachabilityGraphNode, ReachabilityGraphNode> prev = new HashMap<>();
+               List<Set<Transition>> shortest = new ArrayList<>();
+               List<ReachabilityGraphNode> path = new ArrayList<>();
+               final int INFINITY = 1000;
+               Map<ReachabilityGraphNode, Integer> map = new HashMap<>();
+               map.put(s, 0);
+
+               // Distance from source to source
+               dist.put(s, 0);
+               prev.put(t, null);
+               LinkedList<ReachabilityGraphNode> worklist = new LinkedList<>();
+               int cnt = 0;
+               long startNode = System.nanoTime();
+
+               for (ReachabilityGraphNode v : this.getNodes()) {
+                       if (!v.equals(s)) {
+                               dist.put(v, INFINITY);
+                               prev.put(v, null);
+                       }
+                       worklist.add(v);
+                       cnt++;
+               }
+               System.out.println("reachable nodes: " + cnt);
+               long endNode = System.nanoTime();
+               TimeUtil.reportTime(startNode, endNode, "Time on all nodes: ");
+
+               long startShort = System.nanoTime();
+               while (!worklist.isEmpty()) {
+                       ReachabilityGraphNode u = s;
+                       int min = INFINITY;
+                       for (ReachabilityGraphNode v : worklist) {
+                               if (dist.get(v) < min) {
+                                       min = dist.get(v);
+                                       u = v;
+                               }
+                       }
+
+                       boolean flag = worklist.remove(u);
+                       assert flag : "remove fail.";
+                       if (u.equals(t))
+                               break;
+
+                       assert map.containsKey(u);
+                       int depth = map.get(u);
+
+//                     System.out.println("depth: " + depth);
+                       depth++;
+                       for (ReachabilityGraphNode succ : u.getPostset()) {
+                               int alt = dist.get(u) + 1;
+
+                               map.put(succ, depth);
+                               if (alt < dist.get(succ)) {
+                                       dist.put(succ, alt);
+                                       prev.put(succ, u);
+                               }
+                       }
+               }
+
+               // exist a path from source to target?
+               if (prev.get(t) != null) {
+                       path.add(t);
+                       ReachabilityGraphNode cur = t;
+                       while (!cur.equals(s)) {
+                               cur = prev.get(cur);
+                               path.add(cur);
+                       }
+                       assert path.contains(s);
+               }
+
+               // collect all transitions.
+
+               if (path.size() < 2)
+                       return shortest;
+
+               Collections.reverse(path);
+               ReachabilityGraphNode init = path.get(0);
+
+               for (int i = 1; i < path.size(); i++) {
+                       ReachabilityGraphNode st = path.get(i);
+                       Set<Transition> set = new HashSet<>();
+                       for (ReachabilityGraphEdge e : init.getPostsetEdgesByTgt(st)) {
+                               set.add(e.getTransition());
+                       }
+                       shortest.add(set);
+                       init = st;
+               }
+               
+               long endShort = System.nanoTime();
+               TimeUtil.reportTime(startShort, endShort, "Time on shortest path: ");
+
+               return shortest;
+       }
+
+       // use DFS
+       private void enumerate(ReachabilityGraphNode v, ReachabilityGraphNode t) {
+               path.push(v);
+               onPath.add(v);
+               if (v.equals(t)) {
+                       Stack<ReachabilityGraphNode> temp = new Stack<>();
+                       temp.addAll(path);
+                       paths.add(temp);
+               } else {
+                       for (ReachabilityGraphNode w : v.getPostset()) {
+                               if (!onPath.contains(w))
+                                       enumerate(w, t);
+                       }
+               }
+
+               path.pop();
+               onPath.remove(v);
+       }
+
+       private Stack<ReachabilityGraphNode> reverse(Stack<ReachabilityGraphNode> st) {
+               Stack<ReachabilityGraphNode> newSt = new Stack<>();
+               while (!st.isEmpty()) {
+                       newSt.push(st.pop());
+               }
+               return newSt;
+
+       }
+
+       private boolean bounded(Marking mk) {
+               return (mk.sumToken() <= K);
+       }
+
+       // filter duplicated paths.
+       public List<Transition> partialOrderReduce(List<List<Set<Transition>>> list) {
+               // prefer longer path
+               List<Transition> sortedList = new ArrayList<>();
+               for (Transition t : pn.getTransitions()) {
+                       if (!t.getId().contains("clone"))
+                               sortedList.add(t);
+               }
+               // filter duplicated.
+               Collections.sort(sortedList, new Comparator<Transition>() {
+                       public int compare(Transition s1, Transition s2) {
+                               // Write your logic here.
+                               return s1.getId().compareTo(s2.getId());
+                       }
+               });
+
+               List<Transition> validSol = new ArrayList<>();
+
+               for (List<Set<Transition>> sol : list) {
+                       // dont prefer short path.
+                       if (sol.size() < sortedList.size())
+                               continue;
+
+                       for (Set<Transition> set : sol) {
+                               if (set.size() == 1)
+                                       validSol.add(set.iterator().next());
+                               else {
+                                       for (Transition tr : set) {
+                                               if (sortedList.contains(tr)) {
+                                                       validSol.add(tr);
+                                                       sortedList.remove(tr);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+
+                       break;
+               }
+
+               System.out.println("sorted List:" + sortedList);
+               System.out.println("valid List:" + validSol);
+               return validSol;
+       }
+
+}
\ No newline at end of file