/// <summary> /// Removes states that do not have an possible input transitions to a /// terminating state /// </summary> /// <param name="partial"></param> /// <returns></returns> public PartialDFA RemoveDeadStates( PartialDFA partial) { var terminable = partial.Terminals.ToSet(Comparer); var unknown = partial.States.Where(s => !terminable.Contains(s)).ToSet(Comparer); var queue = new Queue <TState>(terminable); while (queue.Any()) { var current = queue.Dequeue(); var sourceStates = partial.Transitions .Where(t => terminable.Contains(t.Target)) .Where(t => unknown.Contains(t.Source)) .Select(t => t.Source); foreach (var state in sourceStates) { terminable.Add(state); unknown.Remove(state); queue.Enqueue(state); } } if (!unknown.Any()) { return(partial); } var transitions = from transition in partial.Transitions where !unknown.Contains(transition.Source) where !unknown.Contains(transition.Target) select transition; return(new PartialDFA( partial.Initial, partial.Terminals.ToList(), transitions.ToList())); }
/// <summary> /// Uses the equivalence minimization algorithm to reduce duplicate states /// </summary> /// <param name="partial"></param> /// <returns></returns> public PartialDFA EquivalenceMinimization( PartialDFA partial) { // Initialize with two groups from terminals and everything else var counter = 0; var groups = new Dictionary <Set <TState>, int> { { partial.Terminals.ToSet(Comparer), counter++ }, { partial.States.Where(s => !partial.Terminals.Contains(s)).ToSet(Comparer), counter++ } }; var marked = new Set <Set <TState> >(); var unmarked = new Queue <Set <TState> >(groups.Keys); // Continue until all sets are marked while (unmarked.Any()) { var current = unmarked.Dequeue(); if (current.Count == 1) { // Can't reduce a set of one marked.Add(current); continue; } var table = from state in current let transitons = ( from input in partial.Inputs let targets = partial.Transitions.Where(t => t.Source.Equals(state) && t.Input.Equals(input)).ToList() let targetGroup = targets.Any() ? groups.Keys.Single(g => g.Contains(targets.Single().Target)) : null let targetGroupId = !(targetGroup is null) ? groups[targetGroup] : -1 select targetGroupId ).ToSet() select( State: state, Transitions: transitons, Hash: transitons.GetSequentialHashCode() ); var partitions = from row in table group row by row.Hash into rowsByHash select rowsByHash; var newGroups = partitions .Select(grp => new Set <TState>(grp.Select(g => g.State), Comparer)) .ToList(); if (newGroups.Count == 1) { marked.Add(current); continue; } // Move all marked groups back to unmarked foreach (var markedGroup in marked) { unmarked.Enqueue(markedGroup); } marked.Clear(); // Current group is getting partitioned, to remove it groups.Remove(current); foreach (var newGroup in newGroups) { groups.Add(newGroup, counter++); unmarked.Enqueue(newGroup); } } var terminals = from terminal in partial.Terminals select groups.Keys.Single(g => g.Contains(terminal)); var transitions = from sourceGroup in groups.Keys from input in partial.Inputs let source = sourceGroup.First() where partial.Transitions.Any(t => t.Source.Equals(source) && t.Input.Equals(input)) let target = partial.Transitions.Single(t => t.Source.Equals(source) && t.Input.Equals(input)).Target let targetGroup = groups.Keys.Single(g => g.Contains(target)) select GroupTransition(sourceGroup, input, targetGroup); return(Reduce( groups.Keys.Single(g => g.Contains(partial.Initial)), terminals, transitions )); }