|
|
using Implab;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Linq;
|
|
|
|
|
|
namespace Implab.Automaton {
|
|
|
public class DFADefinition<TInput, TState, TTag> : IDFADefinitionBuilder<TTag>, IDFADefinition<TInput, TState, TTag> {
|
|
|
DFAStateDescriptior<TTag>[] m_dfaTable;
|
|
|
readonly IAlphabet<TInput> m_inputAlphabet;
|
|
|
readonly IAlphabet<TState> m_stateAlphabet;
|
|
|
|
|
|
readonly Dictionary<int, TTag[]> m_finalStates = new Dictionary<int, TTag[]>();
|
|
|
readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>();
|
|
|
|
|
|
public DFADefinition(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) {
|
|
|
Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet");
|
|
|
Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet");
|
|
|
|
|
|
m_inputAlphabet = inputAlphabet;
|
|
|
m_stateAlphabet = stateAlphabet;
|
|
|
}
|
|
|
|
|
|
public void DefineTransition(int s1, int s2, int symbol) {
|
|
|
Safe.ArgumentInRange(s1, 0, m_stateAlphabet.Count-1, "s1");
|
|
|
Safe.ArgumentInRange(s2, 0, m_stateAlphabet.Count-1, "s2");
|
|
|
Safe.ArgumentInRange(symbol, 0, m_inputAlphabet.Count-1, "symbol");
|
|
|
|
|
|
m_transitions.Add(new AutomatonTransition(s1, s2, symbol));
|
|
|
}
|
|
|
|
|
|
|
|
|
#region IDFADefinition implementation
|
|
|
|
|
|
public DFAStateDescriptior<TTag>[] GetTransitionTable() {
|
|
|
if (m_dfaTable == null) {
|
|
|
m_dfaTable = new DFAStateDescriptior<TTag>[m_stateAlphabet.Count];
|
|
|
|
|
|
foreach (var pair in m_finalStates) {
|
|
|
var idx = pair.Key;
|
|
|
|
|
|
m_dfaTable[idx].final = true;
|
|
|
m_dfaTable[idx].tag = m_dfaTable[idx].tag != null ?
|
|
|
m_dfaTable[idx].tag.Concat(pair.Value).Distinct().ToArray() :
|
|
|
pair.Value;
|
|
|
}
|
|
|
|
|
|
foreach (var t in m_transitions) {
|
|
|
if (m_dfaTable[t.s1].transitions == null) {
|
|
|
m_dfaTable[t.s1].transitions = new int[m_inputAlphabet.Count];
|
|
|
for (int i = 0; i < m_dfaTable[t.s1].transitions.Length; i++)
|
|
|
m_dfaTable[t.s1].transitions[i] = DFAConst.UNREACHABLE_STATE;
|
|
|
}
|
|
|
|
|
|
m_dfaTable[t.s1].transitions[t.edge] = t.s2;
|
|
|
}
|
|
|
}
|
|
|
return m_dfaTable;
|
|
|
}
|
|
|
|
|
|
public IAlphabet<TInput> InputAlphabet {
|
|
|
get {
|
|
|
return m_inputAlphabet;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public IAlphabet<TState> StateAlphabet {
|
|
|
get {
|
|
|
return m_stateAlphabet;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region IDFADefinitionBuilder
|
|
|
|
|
|
public void DefineTransition(int s1, int s2, int symbol) {
|
|
|
Safe.ArgumentInRange(s1, 0, m_stateAlphabet.Count - 1, "s1");
|
|
|
Safe.ArgumentInRange(s2, 0, m_stateAlphabet.Count - 1, "s2");
|
|
|
Safe.ArgumentInRange(symbol, 0, m_inputAlphabet.Count - 1, "symbol");
|
|
|
|
|
|
m_transitions.Add(new AutomatonTransition(s1, s2, symbol));
|
|
|
}
|
|
|
|
|
|
public void MarkFinalState(int state, params TTag[] tags) {
|
|
|
m_finalStates[state] = tags;
|
|
|
}
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
protected void Optimize(IDFADefinitionBuilder<TTag> optimalDFA,IAlphabetBuilder<TInput> optimalInputAlphabet, IAlphabetBuilder<TState> optimalStateAlphabet) {
|
|
|
Safe.ArgumentNotNull(optimalDFA, "dfa");
|
|
|
Safe.ArgumentNotNull(optimalInputAlphabet, "optimalInputAlphabet");
|
|
|
Safe.ArgumentNotNull(optimalStateAlphabet, "optimalStateAlphabet");
|
|
|
|
|
|
var setComparer = new CustomEqualityComparer<HashSet<int>>(
|
|
|
(x, y) => x.SetEquals(y),
|
|
|
s => s.Sum(x => x.GetHashCode())
|
|
|
);
|
|
|
|
|
|
var arrayComparer = new CustomEqualityComparer<TTag[]>(
|
|
|
(x,y) => (new HashSet<int>(x)).SetEquals(new HashSet<int>(y)),
|
|
|
a => a.Sum(x => x.GetHashCode())
|
|
|
);
|
|
|
|
|
|
var optimalStates = new HashSet<HashSet<int>>(setComparer);
|
|
|
var queue = new HashSet<HashSet<int>>(setComparer);
|
|
|
|
|
|
// получаем конечные состояния, сгруппированные по маркерам
|
|
|
optimalStates.UnionWith(
|
|
|
m_finalStates
|
|
|
.GroupBy(pair => pair.Value, arrayComparer)
|
|
|
.Select(
|
|
|
g => new HashSet<int>(
|
|
|
g.Select( pair => pair.Key)
|
|
|
)
|
|
|
)
|
|
|
);
|
|
|
|
|
|
var state = new HashSet<int>(
|
|
|
Enumerable
|
|
|
.Range(0, m_stateAlphabet.Count - 1)
|
|
|
.Where(i => !m_finalStates.ContainsKey(i))
|
|
|
);
|
|
|
optimalStates.Add(state);
|
|
|
queue.Add(state);
|
|
|
|
|
|
var rmap = m_transitions
|
|
|
.GroupBy(t => t.s2)
|
|
|
.ToLookup(
|
|
|
g => g.Key, // s2
|
|
|
g => g.ToLookup(t => t.edge, t => t.s1)
|
|
|
);
|
|
|
|
|
|
while (queue.Count > 0) {
|
|
|
var stateA = queue.First();
|
|
|
queue.Remove(stateA);
|
|
|
|
|
|
for (int c = 0; c < m_inputAlphabet.Count; c++) {
|
|
|
var stateX = new HashSet<int>();
|
|
|
foreach(var a in stateA)
|
|
|
stateX.UnionWith(rmap[a][c]); // all states from wich 'c' leads to 'a'
|
|
|
|
|
|
foreach (var stateY in optimalStates.ToArray()) {
|
|
|
if (stateX.Overlaps(stateY) && !stateY.IsSubsetOf(stateX)) {
|
|
|
var stateR1 = new HashSet<int>(stateY);
|
|
|
var stateR2 = new HashSet<int>(stateY);
|
|
|
|
|
|
stateR1.IntersectWith(stateX);
|
|
|
stateR2.ExceptWith(stateX);
|
|
|
|
|
|
optimalStates.Remove(stateY);
|
|
|
optimalStates.Add(stateR1);
|
|
|
optimalStates.Add(stateR2);
|
|
|
|
|
|
if (queue.Contains(stateY)) {
|
|
|
queue.Remove(stateY);
|
|
|
queue.Add(stateR1);
|
|
|
queue.Add(stateR2);
|
|
|
} else {
|
|
|
queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// карта получения оптимального состояния по соотвествующему ему простому состоянию
|
|
|
var statesMap = m_stateAlphabet.Reclassify(optimalStateAlphabet, optimalStates);
|
|
|
|
|
|
// получаем минимальный алфавит
|
|
|
// входные символы не различимы, если Move(s,a1) == Move(s,a2)
|
|
|
var optimalAlphabet = m_transitions
|
|
|
.GroupBy(t => Tuple.Create(statesMap[t.s1], statesMap[t.s2]), t => t.edge);
|
|
|
|
|
|
var alphabetMap = m_inputAlphabet.Reclassify(optimalInputAlphabet, optimalAlphabet);
|
|
|
|
|
|
var optimalTags = m_finalStates
|
|
|
.GroupBy(pair => statesMap[pair.Key])
|
|
|
.ToDictionary(
|
|
|
g => g.Key,
|
|
|
g => g.SelectMany(pair => pair.Value).ToArray()
|
|
|
);
|
|
|
|
|
|
// построение автомата
|
|
|
foreach (var pair in optimalTags)
|
|
|
optimalDFA.MarkFinalState(pair.Key, pair.Value);
|
|
|
|
|
|
foreach (var t in m_transitions.Select(t => new AutomatonTransition(statesMap[t.s1],statesMap[t.s2],alphabetMap[t.edge])).Distinct())
|
|
|
optimalDFA.DefineTransition(t.s1, t.s2, t.edge);
|
|
|
}
|
|
|
|
|
|
public void PrintDFA() {
|
|
|
|
|
|
var inputMap = InputAlphabet.CreateReverseMap();
|
|
|
var stateMap = StateAlphabet.CreateReverseMap();
|
|
|
|
|
|
for (int i = 0; i < inputMap.Length; i++) {
|
|
|
Console.WriteLine("C{0}: {1}", i, String.Join(",", inputMap[i]));
|
|
|
}
|
|
|
|
|
|
foreach(var t in m_transitions)
|
|
|
Console.WriteLine(
|
|
|
"S{0} -{1}-> S{2}{3}",
|
|
|
stateMap[t.s1],
|
|
|
String.Join(",", inputMap[t.edge]),
|
|
|
stateMap[t.s2],
|
|
|
m_finalStates.ContainsKey(t.s2) ? "$" : ""
|
|
|
);
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|