From fa8f9f79320df397c9e8cba7132941783bbf734e Mon Sep 17 00:00:00 2001 From: DavePearce Date: Fri, 20 Nov 2015 08:42:35 +1100 Subject: [PATCH] Incorporated simple optimisations #19 These optimisations are based on those used in v0.3.36. They are still relatively simple but focus on reducing the amount of memory needlessly allocated during reductions. They still traverse the entire automaton after every reduction though, which is wasteful. --- src/wyrw/util/Reductions.java | 143 +++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 2 deletions(-) diff --git a/src/wyrw/util/Reductions.java b/src/wyrw/util/Reductions.java index 178382f..c6b08f9 100644 --- a/src/wyrw/util/Reductions.java +++ b/src/wyrw/util/Reductions.java @@ -39,6 +39,7 @@ public static void reduce(Automaton automaton, int maxSteps, ReductionRule... re public static void reduceOver(Automaton automaton, int start, int maxSteps, ReductionRule[] reductions, Comparator comparator) { // Now, attempt to reduce as much as possible + boolean[] reachable = new boolean[automaton.nStates()*2]; boolean changed = true; while (changed && maxSteps-- > 0) { changed = false; @@ -50,8 +51,20 @@ public static void reduceOver(Automaton automaton, int start, int maxSteps, Redu int target = activation.apply(automaton); if (target != Automaton.K_VOID && from != target) { // Rewrite applied - //automaton.minimise(); - automaton.compact(0); + //automaton.compact(0); + // Update reachability status for nodes affected by this + // activation. This is because such states could cause + // an infinite loop of re-activations. More specifically, where + // we activate on a state and rewrite it, but then it remains + // and so we repeat. + reachable = initReachable(automaton, reachable); + + // Compact all states above the pivot to eliminate unreachable + // states and prevent the automaton from growing continually. + // This is possible because automton.rewrite() can introduce + // null states into the automaton. + compact(automaton, pivot, reachable); + // changed = true; break; } else { @@ -59,6 +72,8 @@ public static void reduceOver(Automaton automaton, int start, int maxSteps, Redu } } } + + compact(automaton, 0, reachable); } private static AbstractActivation[] probe(Automaton automaton, int start, ReductionRule[] reductions, @@ -82,4 +97,128 @@ private static AbstractActivation[] probe(Automaton automaton, int start, Reduct } return array; } + + + private void compact(Automaton automaton, int pivot) { + + } + + + /** + * Update the reachability information associated with the automaton after + * some change has occurred. This information is currently recomputed from + * scratch, though in principle it could be updated incrementally. + */ + private static boolean[] initReachable(Automaton automaton, + boolean[] reachable) { + + // TODO: update reachability information incrementally + + if (reachable.length < automaton.nStates()) { + reachable = new boolean[automaton.nStates() * 2]; + } else { + Arrays.fill(reachable, false); + } + // first, visit all nodes + for (int i = 0; i != automaton.nRoots(); ++i) { + int root = automaton.getRoot(i); + if (root >= 0) { + findReachable(automaton, reachable, root); + } + } + + return reachable; + } + + /** + * Visit all states reachable from a given starting state in the given + * automaton. In doing this, states which are visited are marked and, + * furthermore, those which are "headers" are additionally identified. A + * header state is one which is the target of a back-edge in the directed + * graph reachable from the start state. + * + * @param automaton + * --- automaton to traverse. + * @param reachable + * --- states marked with false are those which have not been + * visited. + * @param index + * --- state to begin traversal from. + * @return + */ + public static void findReachable(Automaton automaton, boolean[] reachable, + int index) { + if (index < 0) { + return; + } else if (reachable[index]) { + // Already visited, so terminate here + return; + } else { + // Not previously visited, so mark now and traverse any children + reachable[index] = true; + Automaton.State state = automaton.get(index); + if (state instanceof Automaton.Term) { + Automaton.Term term = (Automaton.Term) state; + if (term.contents != Automaton.K_VOID) { + findReachable(automaton, reachable, term.contents); + } + } else if (state instanceof Automaton.Collection) { + Automaton.Collection compound = (Automaton.Collection) state; + for (int i = 0; i != compound.size(); ++i) { + findReachable(automaton, reachable, compound.get(i)); + } + } + } + } + + private static void compact(Automaton automaton, int pivot, + boolean[] reachable) { + int nStates = automaton.nStates(); + int nRoots = automaton.nRoots(); + int[] binding = new int[nStates]; + + // First, initialise binding for all states upto start state. This + // ensure that they are subsequently mapped to themselves. + for (int i = 0; i < nStates; ++i) { + binding[i] = i; + } + + // Second, go through and eliminate all unreachable states and compact + // the automaton down, whilst updating reachable one oneStepUndo + // information accordingly. + int j = pivot; + + for (int i = pivot; i < nStates; ++i) { + if (reachable[i]) { + Automaton.State ith = automaton.get(i); + binding[i] = j; + reachable[i] = false; + reachable[j] = true; + automaton.set(j++, ith); + } + } + + if (j < nStates) { + // Ok, some compaction actually occurred; therefore follow through + // and update all states accordingly. + nStates = j; + automaton.resize(nStates); // will nullify all deleted states + + // Update mapping and oneStepUndo for *all* states + for (int i = 0; i != nStates; ++i) { + Automaton.State state = automaton.get(i); + if(state != null) { + state.remap(binding); + } + } + + // Update mapping for all roots + for (int i = 0; i != nRoots; ++i) { + int root = automaton.getRoot(i); + if (root >= 0) { + automaton.setRoot(i, binding[root]); + } + } + } + } }