internal static Automaton AddXSurroundedPaths(this Automaton automaton) { var states = new List <int>(automaton.States); var symbols = new[] { Constants.RegularLanguage.R, Constants.RegularLanguage.S }; foreach (var fromState in states) { foreach (var symbol in symbols) { var toStates = new List <int>(automaton.TransitionMatrix.GetStates(fromState, symbol)); foreach (var toState in toStates) { // Add a new state before and after int beforeState = automaton.AddState(); int afterState = automaton.AddState(); // Now add the new transitions between the states automaton.AddTransition(fromState, beforeState, Constants.RegularLanguage.X); automaton.AddTransition(beforeState, afterState, symbol); automaton.AddTransition(afterState, toState, Constants.RegularLanguage.X); } } } // Add epsilon transitions between all new X states bool changes = true; while (changes) { changes = AddEpsilonStatesForXXSubPaths(automaton); } return(automaton); }
private static bool AddEpsilonStatesForXXSubPaths(Automaton automaton) { var changes = false; foreach (var state in automaton.States) { var states = automaton.GetStatesReachableFromStateWithSymbol(state, Constants.RegularLanguage.X, false); foreach (var xReachableState in states) { var epsilonStates = automaton.GetStatesReachableFromStateWithSymbol(xReachableState, Constants.RegularLanguage.X, false, false); foreach (var epsilonState in epsilonStates) { // Discard epsilon transition that loop back on the same state as they add no value if (state != epsilonState) { if (automaton.AddTransition(state, epsilonState, Automaton.Epsilon)) { changes = true; } } } } } return(changes); }
private static bool AddTransitionsFromReachabilityStatus(Automaton automaton, int startState, int finalState, ReachabilityStatus reachabilityStatus) { bool changes = false; if (reachabilityStatus.EvenReachable) { if (automaton.AddTransition(startState, finalState, Constants.RegularLanguage.X)) { changes = true; } } else if (reachabilityStatus.OddReachable) { // Odd reachable so add X transition if (startState != finalState && automaton.AddTransition(startState, finalState, Automaton.Epsilon)) { changes = true; } } return(changes); }
public static Automaton Parse(string file) { Automaton automaton = new Automaton(); int i = 0; using (StreamReader sr = new StreamReader(file)) { string[] inputs = new string[3]; while (!sr.EndOfStream) { var line = sr.ReadLine().Split(new string[] { "," }, StringSplitOptions.None); i++; if (i == 1) { automaton.EntryState = automaton.GetOrCreateState(line[1].Trim()); } else if (i == 2) { automaton.ExitState = automaton.GetOrCreateState(line[1].Trim()); } else if (i == 3) { automaton.DefaultOutput = line[1].Trim(); } else if (i == 6) { inputs[0] = line[1].Trim(); inputs[1] = line[2].Trim(); inputs[2] = line[3].Trim(); } else if (i > 6) { string from = line[0].Trim(); for (int col = 0; col < 3; col++) { string to = line[col + 1].Trim(); string input = inputs[col].Trim(); string output = line[col + 5].Trim(); automaton.AddTransition(automaton.GetOrCreateState(from), automaton.GetOrCreateState(to), input, output); } } } } return(automaton); }
internal static Automaton CreateLargeAutomataWithRandomTransitions(char[] alphabet, int states = 10000) { var automaton = new Automaton(alphabet); var rng = new Random(); for (int i = 0; i < states; i++) { automaton.AddState(); } automaton.SetAsStartState(0); automaton.SetAsFinalState(states - 1); for (int i = 0; i < states; i++) { foreach (var symbol in alphabet) { var state = rng.Next(states); automaton.AddTransition(i, state, symbol); } } return(automaton); }
/// <summary> /// Adds a transition to the internally stored canonical state substrings. Returns a list of all transitions that should be added directly resulting from this /// </summary> public IReadOnlyCollection <Transition> AddTransition(int stateFrom, char symbol, int stateTo) { // This process works as follows: // - Check whether the transition already exists. If it already does, then just return. // - Add the transition to the lookup // - Calculate newly created transitions from the lookups var outTransitionLookup = _outgoingTransitionLookup[stateFrom].GetTransitionDictionary(symbol); var inTransitionLookup = _incomingTransitionLookup[stateTo].GetTransitionDictionary(symbol); // Check whether the transition already exists, and add it if not. // We will check the outgoing transition from the stateFrom, but we expect it to be consistent between the outgoing and incoming transition // I.e. if s0 ----S----> s1, then we have S outgoing from s0 to s1, and S incoming on s1 from s0 // Therefore, if something breaks here then we know that our code has produced an inconsistent state ReachabilityStatus fromReachabilityStatus; if (outTransitionLookup.TryGetValue(stateTo, out var toReachabilityStatus)) { fromReachabilityStatus = inTransitionLookup[stateFrom]; } else { toReachabilityStatus = new ReachabilityStatus(); outTransitionLookup[stateTo] = toReachabilityStatus; fromReachabilityStatus = new ReachabilityStatus(); inTransitionLookup[stateFrom] = fromReachabilityStatus; } if (symbol != Constants.RegularLanguage.X) { if (toReachabilityStatus.EvenReachable) { // Transition already exists return(Array.Empty <Transition>()); } else { fromReachabilityStatus.EvenReachable = true; toReachabilityStatus.EvenReachable = true; } } else { if (toReachabilityStatus.OddReachable) { // Transition already exists return(Array.Empty <Transition>()); } else { toReachabilityStatus.OddReachable = true; fromReachabilityStatus.OddReachable = true; } } var toAddTransitions = new List <Transition>(); Action <int, int, char> addTransition = (fromState, toState, symbol) => { if (fromState == toState && symbol == Automaton.Epsilon) { return; } if (!_automaton.AddTransition(fromState, toState, symbol)) { return; } toAddTransitions.Add(new Transition(fromState, toState, symbol)); }; // Adds an X or epsilon transition from a given state to another, based on an existing reachability status Action <int, int, ReachabilityStatus, bool> addTransitionFromReachabilityStatus = (fromState, toState, reachabilityStatus, negated) => { if ((reachabilityStatus.EvenReachable && negated) || (reachabilityStatus.OddReachable && !negated)) { addTransition(fromState, toState, Constants.RegularLanguage.X); } if ((reachabilityStatus.OddReachable && negated) || (reachabilityStatus.EvenReachable && !negated)) { addTransition(fromState, toState, Automaton.Epsilon); } }; Func <int, Dictionary <int, ReachabilityStatus> > getEpsilonReachabilityDictionary = (state) => { var rightReachabilityDictionary = _outgoingTransitionLookup[state].EpsilonTransitions.ToDictionary(kv => kv.Key, kv => kv.Value); ReachabilityStatus?reachabilityStatus = null; if (!rightReachabilityDictionary.TryGetValue(stateTo, out reachabilityStatus)) { reachabilityStatus = new ReachabilityStatus(); rightReachabilityDictionary[stateTo] = reachabilityStatus; } reachabilityStatus.EvenReachable |= true; return(rightReachabilityDictionary); }; // Adds transitions for each side combined together with the middle. Should only be called on combinations that make sense. Action <Dictionary <int, ReachabilityStatus>, char, int, bool> combineWithBothSides = (incomingReachabilityLookup, symbol, rightSymbolCount, negated) => { var rightReachabilityDictionary = getEpsilonReachabilityDictionary(stateTo); for (int i = 1; i <= rightSymbolCount; i++) { rightReachabilityDictionary = ApplyTransitionToReachabilityDictionary(rightReachabilityDictionary, symbol, i != rightSymbolCount); } foreach (var leftReachabilityStatus in incomingReachabilityLookup) { foreach (var rightReachabilityStatus in rightReachabilityDictionary) { var stateReachabilityStatus = leftReachabilityStatus.Value.Times(rightReachabilityStatus.Value); addTransitionFromReachabilityStatus(leftReachabilityStatus.Key, rightReachabilityStatus.Key, stateReachabilityStatus, negated); } } }; Action <Dictionary <int, ReachabilityStatus>, bool> combineLeft = (incomingReachabilityLookup, negated) => { foreach (var stateReachabilityLookup in incomingReachabilityLookup) { addTransitionFromReachabilityStatus(stateReachabilityLookup.Key, stateTo, stateReachabilityLookup.Value, negated); } }; Action <char, int, bool> combineRight = (symbol, rightSymbolCount, negated) => { var rightReachabilityDictionary = getEpsilonReachabilityDictionary(stateTo); for (int i = 1; i <= rightSymbolCount; i++) { rightReachabilityDictionary = ApplyTransitionToReachabilityDictionary(rightReachabilityDictionary, symbol, i != rightSymbolCount); } foreach (var rightReachabilityLookup in rightReachabilityDictionary) { addTransitionFromReachabilityStatus(stateFrom, rightReachabilityLookup.Key, rightReachabilityLookup.Value, negated); } }; // Update a given transition set, by iterating through all possibilities specified in the iterate lookup Action <int, Dictionary <int, ReachabilityStatus>, Func <StateTransitions, Dictionary <int, ReachabilityStatus> >, bool> updateTransitionSetEpsilon = (inState, iterateReachabilityLookup, getToReachabilitySet, negated) => { foreach (var stateReachabilityLookup in iterateReachabilityLookup) { var currentOutState = stateReachabilityLookup.Key; var currentReachabilityStatus = stateReachabilityLookup.Value; UpdateReachabilityStatus(currentOutState, currentReachabilityStatus, getToReachabilitySet(_incomingTransitionLookup[inState]), negated); UpdateReachabilityStatus(inState, currentReachabilityStatus, getToReachabilitySet(_outgoingTransitionLookup[currentOutState]), negated); } }; Action <Dictionary <int, ReachabilityStatus>, Func <StateTransitions, Dictionary <int, ReachabilityStatus> >, bool> updateTransitionSetRight = (iterateReachabilityLookup, getToReachabilitySet, negated) => { foreach (var stateReachabilityLookup in iterateReachabilityLookup) { var currentReachabilityStatus = stateReachabilityLookup.Value; UpdateReachabilityStatus(stateFrom, currentReachabilityStatus, getToReachabilitySet(_incomingTransitionLookup[stateReachabilityLookup.Key]), negated); UpdateReachabilityStatus(stateReachabilityLookup.Key, currentReachabilityStatus, getToReachabilitySet(_outgoingTransitionLookup[stateFrom]), negated); } }; Action <Dictionary <int, ReachabilityStatus>, Func <StateTransitions, Dictionary <int, ReachabilityStatus> >, bool> updateTransitionSetLeft = (iterateReachabilityLookup, getToReachabilitySet, negated) => { foreach (var stateReachabilityLookup in iterateReachabilityLookup) { foreach (var epsilonReachability in getEpsilonReachabilityDictionary(stateTo)) { var combinedReachability = stateReachabilityLookup.Value.Times(epsilonReachability.Value); UpdateReachabilityStatus(epsilonReachability.Key, combinedReachability, getToReachabilitySet(_outgoingTransitionLookup[stateReachabilityLookup.Key]), false); UpdateReachabilityStatus(stateReachabilityLookup.Key, combinedReachability, getToReachabilitySet(_incomingTransitionLookup[epsilonReachability.Key]), false); } } }; Action <Dictionary <int, ReachabilityStatus>, Dictionary <int, ReachabilityStatus>, Func <StateTransitions, Dictionary <int, ReachabilityStatus> >, bool> updateTransitionSetBothSides = (leftIterateSet, rightIterateSet, getToReachabilitySet, negated) => { foreach (var lefStateReachabilityLookup in leftIterateSet) { foreach (var rightStateReachabilityLookup in rightIterateSet) { var leftState = lefStateReachabilityLookup.Key; var rightState = rightStateReachabilityLookup.Key; var currentReachabilityStatus = lefStateReachabilityLookup.Value.Times(rightStateReachabilityLookup.Value); UpdateReachabilityStatus(leftState, currentReachabilityStatus, getToReachabilitySet(_incomingTransitionLookup[rightState]), negated); UpdateReachabilityStatus(rightState, currentReachabilityStatus, getToReachabilitySet(_outgoingTransitionLookup[leftState]), negated); } } }; // We have sk ---symbol---> sj. // Now combine this with the incoming and outgoing transitions with sk and sjs switch (symbol) { // For S Combine with S transitions on each side (S + S = X) case Constants.RegularLanguage.S: updateTransitionSetRight(_outgoingTransitionLookup[stateTo].EpsilonTransitions, (s) => s.STransitions, false); // Combine new S transition with any incoming and outgoing S transitions combineRight('S', 1, true); combineLeft(_incomingTransitionLookup[stateFrom].STransitions, true); break; // For R Combine left with R and RR transitions on each side.(R + R = RR, RR + R = R + RR = X, R + R + R = X) // In other words: // For R + RR = X or RR + R = X add a transition // For R + R + R = X add a transition // For R + R = RR update the dictionaries. case Constants.RegularLanguage.R: // Update R + X/epsilon updateTransitionSetRight(_outgoingTransitionLookup[stateTo].EpsilonTransitions, (s) => s.RTransitions, false); // Update R + R transitions either side updateTransitionSetLeft(_incomingTransitionLookup[stateFrom].RTransitions, (s) => s.RRTransitions, false); updateTransitionSetRight(_outgoingTransitionLookup[stateTo].RTransitions, (s) => s.RRTransitions, false); // Combine new R transition with any incoming RR transitions combineRight('R', 2, true); combineLeft(_incomingTransitionLookup[stateFrom].RRTransitions, true); // Update incoming and outgoing transitions for R + R + R combineWithBothSides(_incomingTransitionLookup[stateFrom].RTransitions, 'R', 1, true); // Combine new R transition with any incoming R transitions break; case Constants.RegularLanguage.X: case Automaton.Epsilon: // For X and Epsilon Combine with X, S, R and RR transitions on each side var even = true; if (symbol == Constants.RegularLanguage.X) { // Update chains set foreach (var chainEnd in _outgoingTransitionLookup[stateTo].EpsilonChains.Append(stateTo)) { _outgoingTransitionLookup[stateFrom].XEpsilonChains.Add(chainEnd); _incomingTransitionLookup[chainEnd].XEpsilonChains.Add(stateFrom); } even = false; // Add epsilon transitions for X + X // TODO: Potential optimisation: // We shouldn't need to add the state to the queue here as the state should already be consistent at this point, // so we should just add the state to the automaton, so long as it doesn't go to itself. foreach (var xChainStart in _incomingTransitionLookup[stateFrom].XEpsilonChains) { addTransition(xChainStart, stateTo, Automaton.Epsilon); } var xStates = _automaton.GetStatesReachableFromStateWithSymbol(stateTo, 'X', true, false); foreach (var xState in xStates) { addTransition(stateFrom, xState, Automaton.Epsilon); } } else { // Update chains set _outgoingTransitionLookup[stateFrom].EpsilonChains.Add(stateTo); _incomingTransitionLookup[stateTo].EpsilonChains.Add(stateFrom); foreach (var chainEnd in _outgoingTransitionLookup[stateTo].EpsilonChains.Append(stateTo)) { foreach (var chainStart in _incomingTransitionLookup[stateFrom].XEpsilonChains) { _outgoingTransitionLookup[chainStart].XEpsilonChains.Add(chainEnd); _incomingTransitionLookup[chainEnd].XEpsilonChains.Add(chainStart); } foreach (var chainStart in _incomingTransitionLookup[stateFrom].EpsilonChains) { _outgoingTransitionLookup[chainStart].EpsilonChains.Add(chainEnd); _incomingTransitionLookup[chainEnd].EpsilonChains.Add(chainStart); } } // Combine with X on each side X var incomingXEpsilonChains = _incomingTransitionLookup[stateFrom].XEpsilonChains; var xStates = _automaton.GetStatesReachableFromStateWithSymbol(stateTo, 'X', true, false); var newTransitions = new List <(int from, int to)>(); foreach (var startXState in incomingXEpsilonChains) { foreach (var outgoingX in xStates) { // Both X. Combine. newTransitions.Add((startXState, outgoingX)); } } foreach (var transition in newTransitions) { addTransition(transition.from, transition.to, Automaton.Epsilon); } } // First update the Transition lookup. // Update epsilon transitions updateTransitionSetRight(_outgoingTransitionLookup[stateTo].EpsilonTransitions, (s) => s.EpsilonTransitions, !even); // updateTransitionSetLeft(_incomingTransitionLookup[stateFrom].EpsilonTransitions, (s) => s.EpsilonTransitions, !even); updateTransitionSetEpsilon(stateTo, _incomingTransitionLookup[stateFrom].EpsilonTransitions, (s) => s.EpsilonTransitions, !even); // Combine with X,S,R,RR from front side. We only do the front side as the rules we are checking are XX, SXS, RXRXR. foreach (var reachabilityLookup in _outgoingTransitionLookup[stateFrom].EpsilonTransitions) { if (reachabilityLookup.Value.EvenReachable) { updateTransitionSetEpsilon(reachabilityLookup.Key, _incomingTransitionLookup[stateFrom].STransitions, (s) => s.STransitions, false); updateTransitionSetEpsilon(reachabilityLookup.Key, _incomingTransitionLookup[stateFrom].RTransitions, (s) => s.RTransitions, false); updateTransitionSetEpsilon(reachabilityLookup.Key, _incomingTransitionLookup[stateFrom].RRTransitions, (s) => s.RRTransitions, false); } if (reachabilityLookup.Value.OddReachable) { updateTransitionSetEpsilon(reachabilityLookup.Key, _incomingTransitionLookup[stateFrom].STransitions, (s) => s.STransitions, true); updateTransitionSetEpsilon(reachabilityLookup.Key, _incomingTransitionLookup[stateFrom].RTransitions, (s) => s.RTransitions, true); updateTransitionSetEpsilon(reachabilityLookup.Key, _incomingTransitionLookup[stateFrom].RRTransitions, (s) => s.RRTransitions, true); } } // Combine R + R var rightReachability = getEpsilonReachabilityDictionary(stateTo); rightReachability = ApplyTransitionToReachabilityDictionary(rightReachability, 'R', false); foreach (var incomingRReachability in _incomingTransitionLookup[stateFrom].RTransitions) { foreach (var epsilonReachability in rightReachability) { var combinedReachability = incomingRReachability.Value.Times(epsilonReachability.Value); UpdateReachabilityStatus(epsilonReachability.Key, combinedReachability, _outgoingTransitionLookup[incomingRReachability.Key].RRTransitions, !even); UpdateReachabilityStatus(incomingRReachability.Key, combinedReachability, _incomingTransitionLookup[epsilonReachability.Key].RRTransitions, !even); } } // Combine S + S combineWithBothSides(_incomingTransitionLookup[stateFrom].STransitions, 'S', 1, even); // Combine R + RR combineWithBothSides(_incomingTransitionLookup[stateFrom].RTransitions, 'R', 2, even); // Combine RR + R combineWithBothSides(_incomingTransitionLookup[stateFrom].RRTransitions, 'R', 1, even); break; default: throw new ArgumentException($"Character {symbol} has no understood implementation for canonicalisation"); } return(toAddTransitions); }
public static Automaton Build() { var automaton = new Automaton(); automaton.EntryState = automaton.GetOrCreateState("Nonexistant"); automaton.ExitState = automaton.GetOrCreateState("Discontinued"); automaton.AddTransition(automaton.GetOrCreateState("Nonexistant"), automaton.GetOrCreateState("Available"), "CreateAvailable", "Available"); automaton.AddTransition(automaton.GetOrCreateState("Nonexistant"), automaton.GetOrCreateState("Preorder"), "CreatePreorder", "Preorder"); automaton.AddTransition(automaton.GetOrCreateState("Preorder"), automaton.GetOrCreateState("Preorder"), "ToPreorder", "Preorder"); automaton.AddTransition(automaton.GetOrCreateState("Preorder"), automaton.GetOrCreateState("Available"), "ToAvailable", "Available"); automaton.AddTransition(automaton.GetOrCreateState("Preorder"), automaton.GetOrCreateState("OutOfStock"), "ToOutOfStock", "OutOfStock"); automaton.AddTransition(automaton.GetOrCreateState("Preorder"), automaton.GetOrCreateState("OnTheWay"), "ToOnTheWay", "OnTheWay"); automaton.AddTransition(automaton.GetOrCreateState("Preorder"), automaton.GetOrCreateState("Discontinued"), "ToDiscontinued", "Discontinued"); automaton.AddTransition(automaton.GetOrCreateState("Available"), automaton.GetOrCreateState("Available"), "ToAvailable", "Available"); automaton.AddTransition(automaton.GetOrCreateState("Available"), automaton.GetOrCreateState("OutOfStock"), "ToOutOfStock", "OutOfStock"); automaton.AddTransition(automaton.GetOrCreateState("Available"), automaton.GetOrCreateState("OnTheWay"), "ToOnTheWay", "OnTheWay"); automaton.AddTransition(automaton.GetOrCreateState("Available"), automaton.GetOrCreateState("Discontinued"), "ToDiscontinued", "Discontinued"); automaton.AddTransition(automaton.GetOrCreateState("OutOfStock"), automaton.GetOrCreateState("OutOfStock"), "ToOutOfStock", "OutOfStock"); automaton.AddTransition(automaton.GetOrCreateState("OutOfStock"), automaton.GetOrCreateState("Available"), "ToAvailable", "Available"); automaton.AddTransition(automaton.GetOrCreateState("OutOfStock"), automaton.GetOrCreateState("OnTheWay"), "ToOnTheWay", "OnTheWay"); automaton.AddTransition(automaton.GetOrCreateState("OutOfStock"), automaton.GetOrCreateState("Discontinued"), "ToDiscontinued", "Discontinued"); automaton.AddTransition(automaton.GetOrCreateState("OnTheWay"), automaton.GetOrCreateState("OnTheWay"), "ToOnTheWay", "OnTheWay"); automaton.AddTransition(automaton.GetOrCreateState("OnTheWay"), automaton.GetOrCreateState("Available"), "ToAvailable", "Available"); automaton.AddTransition(automaton.GetOrCreateState("OnTheWay"), automaton.GetOrCreateState("Discontinued"), "ToDiscontinued", "Discontinued"); //automaton.AddTransition(automaton.GetOrCreateState(""), automaton.GetOrCreateState(""), "", ""); return(automaton); }