Skip to content

Commit

Permalink
Incorporated simple optimisations #19
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DavePearce committed Nov 19, 2015
1 parent ee7f5a1 commit fa8f9f7
Showing 1 changed file with 141 additions and 2 deletions.
143 changes: 141 additions & 2 deletions src/wyrw/util/Reductions.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Rewrite.Activation> 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;
Expand All @@ -50,15 +51,29 @@ 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 {
automaton.resize(pivot);
}
}
}

compact(automaton, 0, reachable);
}

private static AbstractActivation[] probe(Automaton automaton, int start, ReductionRule[] reductions,
Expand All @@ -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]);
}
}
}
}
}

0 comments on commit fa8f9f7

Please sign in to comment.