/// <summary> /// Returns true iff the DFA is not complete or not a DFA (misses transitions) /// </summary> /// <param name="dfa"></param> /// <param name="al"></param> /// <param name="solver"></param> /// <returns></returns> public static bool ContainsSyntacticMistake(Automaton <BDD> dfa, HashSet <char> al, CharSetSolver solver, HashSet <int> missingEdges) { bool mistake = false; var dfaNorm = DFAUtilities.normalizeDFA(dfa).First; foreach (var state in dfaNorm.States) { HashSet <char> alCopy = new HashSet <char>(al); foreach (var move in dfaNorm.GetMovesFrom(state)) { foreach (var c in solver.GenerateAllCharacters(move.Label, false)) { if (!alCopy.Contains(c)) { int hash = (int)(Math.Pow(2, move.SourceState) + Math.Pow(3, c - 97)) + dfaNorm.StateCount; mistake = true; } alCopy.Remove(c); } } if (alCopy.Count > 0) { mistake = true; } } return(mistake); }
public static XElement getTwoWordsInstructorFeedback(XElement regex, XElement xAlphabet, XElement first, XElement second) { string firstString = (XElement.Parse(DFAUtilities.RemoveAllNamespaces(first.ToString()))).Value.Trim(); string secondString = (XElement.Parse(DFAUtilities.RemoveAllNamespaces(second.ToString()))).Value.Trim(); HashSet <char> alphabet = parseAlphabet(xAlphabet); CharSetSolver solver = new CharSetSolver(BitWidth.BV64); var dfaPair = DFAUtilities.parseRegexFromXML(regex, xAlphabet, solver); var dfa = dfaPair.Second.Determinize(solver).Minimize(solver); firstString = firstString.decodeEpsilon(); secondString = secondString.decodeEpsilon(); int firstState = DFAUtilities.GetStateAfterString(dfa.InitialState, firstString, dfa, solver); int secondState = DFAUtilities.GetStateAfterString(dfa.InitialState, secondString, dfa, solver); bool areEquivalent = (firstState == secondState); if (areEquivalent) { var suffixDfa = Automaton <BDD> .Create(firstState, dfa.GetFinalStates(), dfa.GetMoves()); var suffixRegex = solver.ConvertToRegex(suffixDfa); suffixRegex = regexToTraditional(suffixRegex); return(XElement.Parse(String.Format("<div><feedback> The words '{0}' and '{1}' are equivalent. The language of suffixes is '{2}'</feedback></div>", firstString, secondString, suffixRegex))); } else { var shortestDiff = DFAUtilities.GetDifferentiatingWord(firstState, secondState, dfa, alphabet, solver).Second; return(XElement.Parse(String.Format("<div><feedback> The words '{0}' and '{1}' are NOT equivalent. The shortest differentiating word is '{2}'</feedback></div>", firstString, secondString, shortestDiff))); } }
public static bool canRemoveEdge(Automaton <BDD> nfa, int sourceState, int targetState, CharSetSolver solver, Pair <IEnumerable <string>, IEnumerable <string> > tests, HashSet <char> al) { List <Move <BDD> > newMoves = new List <Move <BDD> >(); newMoves = nfa.GetMoves().ToList(); bool moveExists = false; //goes through each move from state1 and remove it if it goes to state2 resp. foreach (var move in nfa.GetMovesFrom(sourceState)) { if (move.TargetState == targetState) { newMoves.Remove(move); moveExists = true; } } var newNfa = Automaton <BDD> .Create(nfa.InitialState, nfa.GetFinalStates(), newMoves); if (moveExists) { if (DFAUtilities.ApproximateMNEquivalent(tests, 0.5, newNfa, al, solver)) { return(nfa.IsEquivalentWith(newNfa, solver)); } } return(false); }
/// <summary> /// /// </summary> /// <returns></returns> public NFAEditScript GetNFAOptimalEdit(Automaton <BDD> nfa2) { NFAEditScript editScript = new NFAEditScript(); //Start timer sw.Start(); //Normalize NFAs var normNfaPair = DFAUtilities.normalizeDFA(nfa2); var normNfa2 = normNfaPair.First; var stateNamesMapping = normNfaPair.Second; NFAEditScript bestScript = new NFAEditScript(); bestScript.script = null; // increase depth up to maxMoves for (int depth = 1; true; depth++) { var editList = new List <NFAEdit>(); if (GetNFAEditScriptTimeout( depth, -1, normNfa2, editScript.script, editScript.GetCost(), bestScript)) { // if hits timeout break and return null break; } if (bestScript.script != null) { bestScript.script.Reverse(); sw.Stop(); var mappedEditList = new List <NFAEdit>(); //fix states name because of normalization foreach (var edit in bestScript.script) { NFAEdit mappedEdit = null; if (edit is NFAEditState) { var castEdit = edit as NFAEditState; mappedEdit = new NFAEditState(stateNamesMapping[castEdit.state], castEdit.makeFinal); } if (edit is NFAEditMove) { var castEdit = edit as NFAEditMove; mappedEdit = new NFAEditMove( stateNamesMapping[castEdit.sourceState], stateNamesMapping[castEdit.newTargetState], castEdit.ch); } mappedEditList.Add(mappedEdit); } return(bestScript); } } return(null); }
public DFAEDFeedback(Automaton <BDD> dfaGoal, Automaton <BDD> dfaAttempt, FeedbackLevel level, HashSet <char> alphabet, DFAEditScript script, double utility, CharSetSolver solver) : base(level, alphabet, utility, solver) { var positiveDifference = dfaGoal.Minus(dfaAttempt, solver).Determinize(solver).Minimize(solver); var negativeDifference = dfaAttempt.Minus(dfaGoal, solver).Determinize(solver).Minimize(solver); this.counterexample = DFAUtilities.GenerateShortTerm(positiveDifference.IsEmpty ? negativeDifference : positiveDifference, solver); this.type = FeedbackType.DFAED; this.script = script; }
public static bool compareNfaToRegex(XElement regex, XElement alphabet, XElement attemptNfa) { CharSetSolver solver = new CharSetSolver(BitWidth.BV64); var dfaCorrectPair = DFAUtilities.parseRegexFromXML(regex, alphabet, solver); var attempt = DFAUtilities.parseBlockFromXML(attemptNfa, alphabet, solver); var same = dfaCorrectPair.Second.IsEquivalentWith(attempt.Second, solver); return(same); }
public static bool canRemoveState(Automaton <BDD> nfa, int state, CharSetSolver solver, Pair <IEnumerable <string>, IEnumerable <string> > tests, HashSet <char> al) { var newNfa = Automaton <BDD> .Create(nfa.InitialState, nfa.GetFinalStates(), nfa.GetMoves()); newNfa.RemoveTheState(state); if (DFAUtilities.ApproximateMNEquivalent(tests, 0.5, newNfa, al, solver)) { return(nfa.IsEquivalentWith(newNfa, solver)); } return(false); }
public NFAEDFeedback(Automaton <BDD> nfaGoal, Automaton <BDD> nfaAttempt, FeedbackLevel level, HashSet <char> alphabet, NFAEditScript script, CharSetSolver solver) : base(level, alphabet, solver) { //TODO might have to determinize to do this operations var positiveDifference = nfaGoal.Minus(nfaAttempt, solver).Determinize(solver).Minimize(solver); var negativeDifference = nfaAttempt.Minus(nfaGoal, solver).Determinize(solver).Minimize(solver); this.counterexample = DFAUtilities.GenerateShortTerm(positiveDifference.IsEmpty ? negativeDifference : positiveDifference, solver); this.script = script; }
private static HashSet <char> parseAlphabet(XElement xAlphabet) { xAlphabet = DFAUtilities.RemoveJustNamespaces(xAlphabet); HashSet <char> alphabet = new HashSet <char>(); foreach (var child in xAlphabet.Elements("symbol")) { char element = Convert.ToChar(child.Value.Trim()); alphabet.Add(element); } return(alphabet); }
public static XElement getShortestFeedback(XElement regex, XElement xAlphabet, XElement representative, XElement attemptShortest, XElement maxGrade) { int maxG = int.Parse(maxGrade.Value); CharSetSolver solver = new CharSetSolver(BitWidth.BV64); var dfaPair = DFAUtilities.parseRegexFromXML(regex, xAlphabet, solver); var dfa = dfaPair.Second.Minimize(solver); string representativeString = (XElement.Parse(DFAUtilities.RemoveAllNamespaces(representative.ToString()))).Value.Trim(); representativeString = representativeString.decodeEpsilon(); string attemptShortestString = (XElement.Parse(DFAUtilities.RemoveAllNamespaces(attemptShortest.ToString()))).Value.Trim(); attemptShortestString = attemptShortestString.decodeEpsilon(); int representativeState = DFAUtilities.GetStateAfterString(dfa.InitialState, representativeString, dfa, solver); int attemptShortestState = DFAUtilities.GetStateAfterString(dfa.InitialState, attemptShortestString, dfa, solver); xAlphabet = XElement.Parse(DFAUtilities.RemoveAllNamespaces(xAlphabet.ToString())); HashSet <char> alphabet = parseAlphabet(xAlphabet); string correctShortest = DFAUtilities.GetRepresentative(representativeState, dfa, alphabet, solver); if (correctShortest.Equals(attemptShortestString)) { return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>Correct!</feedback></div>", maxG))); } else if (attemptShortestState == representativeState) { if (representativeString.Equals(attemptShortestString)) { return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>Please provide a different word than the given one</feedback></div>", 0))); } else if (attemptShortestString.Length > correctShortest.Length) { return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>The word '{1}' is in the same equivalence class as '{2}' but there exists a shorter one</feedback></div>", (int)(maxG * 2 / 3), attemptShortestString.emptyToEpsilon(), representativeString.emptyToEpsilon()))); } else { return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>The word '{1}' is in the same equivalence class as '{2}' but there exists a <strong>lexicographically smaller</strong> one</feedback></div>", (int)(maxG * 4 / 5), attemptShortestString.emptyToEpsilon(), representativeString.emptyToEpsilon()))); } } else { string feedString = "Incorrect!";//"The Correct Answer is '" + correctShortest + "'"; return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>{1}</feedback></div>", 0, feedString))); } }
public static Tuple <int, int, string> recursiveHelper(XElement regex, XElement alphabet, XElement attemptRoot) { var cleanRegex = XElement.Parse(DFAUtilities.RemoveAllNamespaces(regex.ToString())).Value.Trim(); var subexpressions = GetSubexpressions(cleanRegex); int total = 1, correct = 0; string feedString = ""; if (!compareNfaToRegex(regex, alphabet, attemptRoot)) { if (!wrong.Contains(cleanRegex)) { feedString += String.Format("<li> Incorrect definition of '{0}' </li>", cleanRegex); } wrong.Add(cleanRegex); } else if (!expressions.Contains(cleanRegex)) { correct++; } expressions.Add(cleanRegex); var states = attemptRoot.Element("stateSet"); foreach (var s in states.Elements()) { if (s.Name == "block") { var subreg = s.Attribute("regex").Value.Trim(); if (subexpressions.Contains(subreg)) { var tup = recursiveHelper(XElement.Parse("<div>" + subreg + "</div>"), alphabet, s); correct += tup.Item1; total += tup.Item2; feedString += tup.Item3; } else { feedString += String.Format("<li> '{0}' is not a subexpression of '{1}' </li>", subreg, cleanRegex); } } } return(Tuple.Create(correct, total, feedString)); }
public override string ToString() { string posWitness = null; string negWitness = null; if (!positiveDifference.IsEmpty) { posWitness = DFAUtilities.GenerateShortTerm(positiveDifference, solver); return(string.Format("Your NFA does not accept the {0} while the correct solution does.", posWitness != "" ? "string '<i>" + posWitness + "</i>'" : "empty string")); } else { negWitness = DFAUtilities.GenerateShortTerm(negativeDifference, solver); return(string.Format("Your NFA accepts the {0} while the correct solution doesn't.", negWitness != "" ? "string '<i>" + negWitness + "</i>'" : "empty string")); } }
public static bool canCollapseStates(Automaton <BDD> nfa, int state1, int state2, CharSetSolver solver, Pair <IEnumerable <string>, IEnumerable <string> > tests, HashSet <char> al) { var density = DFADensity.GetDFADensity(nfa, al, solver); // collapses state2 to state1 List <Move <BDD> > newMoves = new List <Move <BDD> >(); foreach (var move in nfa.GetMoves()) { var newSource = move.SourceState; var newTarget = move.TargetState; if (newSource == state2) { newSource = state1; } if (newTarget == state2) { newTarget = state1; } newMoves.Add(new Move <BDD>(newSource, newTarget, move.Label)); } // replace state2 with state1 if initial state // no need to remove state2 from final state list, as it is unreachable int newInitialState = nfa.InitialState; if (nfa.InitialState == state2) { newInitialState = state1; } //makes new Nfa and returns collapse state edit if are equiv var newNfa = Automaton <BDD> .Create(newInitialState, nfa.GetFinalStates(), newMoves); if (DFAUtilities.ApproximateMNEquivalent(tests, density, newNfa, al, solver)) { return(nfa.IsEquivalentWith(newNfa, solver)); } return(false); }
public static XElement getFeedback(XElement regex, XElement alphabet, XElement attemptNfa, XElement maxGrade) { var cleanAttempt = DFAUtilities.RemoveJustNamespaces(attemptNfa).Element("block"); expressions = new HashSet <string>(); wrong = new HashSet <string>(); var feedString = "<ul>"; var tup = recursiveHelper(regex, alphabet, cleanAttempt); feedString += tup.Item3 + "</ul>"; var grade = 1.0 * int.Parse(maxGrade.Value) * tup.Item1 / expressions.Count; if (grade == int.Parse(maxGrade.Value)) { feedString = "Correct!"; } return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>{1}</feedback></div>", (int)(Math.Floor(grade)), feedString))); }
// returns an array where a[n] is the number of paths of length n private static double[] GetPathsUpToN(Automaton <BDD> dfa, HashSet <char> al, CharSetSolver solver, int n) { var normDfa1 = DFAUtilities.normalizeDFA(dfa).First; int length = 0; double[] totPaths = new double[n + 1]; var finalStates = normDfa1.GetFinalStates(); double[] pathNum = new double[normDfa1.StateCount]; pathNum[0] = 1; totPaths[0] = finalStates.Contains(0) ? 1 : 0; for (int i = 1; i < pathNum.Length; i++) { pathNum[i] = 0; } while (length < n) { double[] oldPathNum = pathNum.ToArray(); for (int i = 0; i < pathNum.Length; i++) { pathNum[i] = 0; } length++; foreach (var state in normDfa1.States) { if (oldPathNum[state] > 0) { foreach (var move in normDfa1.GetMovesFrom(state)) { int size = 0; //Check if epsilon transition if (move.Label == null) { size = 1; } else { foreach (var v in solver.GenerateAllCharacters(move.Label, false)) { size++; } } pathNum[move.TargetState] += oldPathNum[state] * size; } } } //totPaths[length] = totPaths[length - 1]; foreach (var state in finalStates) { totPaths[length] += pathNum[state]; } } return(totPaths); }
// looks for an edit at depth "depth" // returns false and null in bestScript if no edit is found at depth "depth" // returns false and not null in bestScript if found // returns true if timeout internal bool GetNFAEditScriptTimeout( int depth, long lastEditHash, Automaton <BDD> currentNfa2, List <NFAEdit> editList, int scriptCost, NFAEditScript bestScript) { // if timeout return true if (sw.ElapsedMilliseconds > timeout) { return(true); } //Stop if no more moves left if (depth == 0) { if (DFAUtilities.ApproximateMNEquivalent(tests, nfa1density, currentNfa2, al, solver) && currentNfa2.IsEquivalentWith(nfa1, solver)) { //check if totalCost < finalScript cost and replace if needed if (bestScript.script == null || scriptCost < bestScript.GetCost()) { bestScript.script = ObjectCopier.Clone <List <NFAEdit> >(editList); } } return(false); } NFAEdit edit = null; long thisEditHash = 0; #region Flip one state from fin to non fin foreach (var state in currentNfa2.States) { thisEditHash = state; if (CanAdd(thisEditHash, lastEditHash)) { //flip its final non final status var newFinalStates = new HashSet <int>(currentNfa2.GetFinalStates()); Automaton <BDD> nfa2new = null; if (currentNfa2.GetFinalStates().Contains(state)) { edit = new NFAEditState(state, false); editList.Insert(0, edit); newFinalStates.Remove(state); nfa2new = Automaton <BDD> .Create(currentNfa2.InitialState, newFinalStates, currentNfa2.GetMoves()); } else { edit = new NFAEditState(state, true); editList.Insert(0, edit); newFinalStates.Add(state); nfa2new = Automaton <BDD> .Create(currentNfa2.InitialState, newFinalStates, currentNfa2.GetMoves()); } if (GetNFAEditScriptTimeout(depth - 1, thisEditHash, nfa2new, editList, scriptCost + edit.GetCost(), bestScript)) { return(true); } editList.RemoveAt(0); } } #endregion #region Change transition from source state currentNfa2 = NFAUtilities.normalizeMoves(currentNfa2, solver); foreach (var sourceState in currentNfa2.States) { HashSet <int> unreachedStates = new HashSet <int>(currentNfa2.States); foreach (var moveFromSource in currentNfa2.GetMovesFrom(sourceState)) { // take all chars in alphabet foreach (var c in al) { long moveHash = currentNfa2.StateCount + IntegerUtil.TripleToInt(sourceState, moveFromSource.TargetState, alphabetMap[c]); thisEditHash = currentNfa2.StateCount + moveHash; if (CanAdd(thisEditHash, lastEditHash)) { BDD cCond = solver.False; BDD newCond = solver.False; //skip epsilon moves if (moveFromSource.Label != null) { // if c in move, remove it and recursion if (solver.Contains(moveFromSource.Label, c)) { cCond = solver.MkNot(solver.MkCharConstraint(false, c)); newCond = solver.MkAnd(moveFromSource.Label, cCond); } else // if c not in move, add it and recursion { cCond = solver.MkCharConstraint(false, c); newCond = solver.MkOr(moveFromSource.Label, cCond); } var newMoves = new List <Move <BDD> >(currentNfa2.GetMoves()); newMoves.Remove(moveFromSource); newMoves.Add(new Move <BDD>(sourceState, moveFromSource.TargetState, newCond)); var nfa2new = Automaton <BDD> .Create(currentNfa2.InitialState, currentNfa2.GetFinalStates(), newMoves); edit = new NFAEditMove(sourceState, moveFromSource.TargetState, c); editList.Insert(0, edit); if (GetNFAEditScriptTimeout(depth - 1, thisEditHash, nfa2new, editList, scriptCost + edit.GetCost(), bestScript)) { return(true); } editList.RemoveAt(0); } } } unreachedStates.Remove(moveFromSource.TargetState); } foreach (var targetState in unreachedStates) { //try adding a symbol not in transition foreach (var c in al) { long moveHash = IntegerUtil.TripleToInt(sourceState, targetState, alphabetMap[c]); thisEditHash = currentNfa2.StateCount + moveHash; var moveCond = solver.MkCharConstraint(false, c); var newMoves = new List <Move <BDD> >(currentNfa2.GetMoves()); newMoves.Add(new Move <BDD>(sourceState, targetState, moveCond)); var nfa2new = Automaton <BDD> .Create(currentNfa2.InitialState, currentNfa2.GetFinalStates(), newMoves); edit = new NFAEditMove(sourceState, targetState, c); editList.Insert(0, edit); //TODO put correct hash if (GetNFAEditScriptTimeout(depth - 1, thisEditHash, nfa2new, editList, scriptCost + edit.GetCost(), bestScript)) { return(true); } editList.RemoveAt(0); } } } #endregion return(false); }
public static XElement getWordsFeedback(XElement regex, XElement xAlphabet, XElement representative, XElement wordsIn, XElement maxGrade) { //read inputs int maxG = int.Parse(maxGrade.Value); List <string> wordsInList = new List <String>(); HashSet <string> usedWords = new HashSet <string>(); foreach (var wordElement in wordsIn.Elements()) { string w = wordElement.Value; if (w.Length > 75) { w = w.Substring(0, 75); //limit word length } w = w.decodeEpsilon(); wordsInList.Add(w); } HashSet <char> alphabet = parseAlphabet(xAlphabet); string alphabetString = alphabetToString(alphabet); CharSetSolver solver = new CharSetSolver(BitWidth.BV64); var dfaPair = DFAUtilities.parseRegexFromXML(regex, xAlphabet, solver); var dfa = dfaPair.Second.Minimize(solver); string representativeString = (XElement.Parse(DFAUtilities.RemoveAllNamespaces(representative.ToString()))).Value.Trim(); representativeString = representativeString.decodeEpsilon(); int representativeState = DFAUtilities.GetStateAfterString(dfa.InitialState, representativeString, dfa, solver); string feedString = "<ul>"; int correct = 0; int total = wordsInList.Count; usedWords.Add(representativeString); foreach (string s in wordsInList) { //TODO: Check string only contains valid characters if (usedWords.Contains(s)) { if (s.Equals(representativeString)) { feedString += String.Format("<li> The word '{0}' was provided by the instructor </li>", s.emptyToEpsilon()); } else { feedString += String.Format("<li> The word '{0}' was already used </li>", s.emptyToEpsilon()); } continue; } bool overValidAlphabet = true; foreach (char c in s) { if (!alphabet.Contains(c)) { overValidAlphabet = false; } } if (!overValidAlphabet) { feedString += String.Format("<li> The word '{0}' is not over the alphabet {1} </li>", s.emptyToEpsilon(), alphabetString); continue; } usedWords.Add(s); int wordState = DFAUtilities.GetStateAfterString(dfa.InitialState, s, dfa, solver); if (wordState == representativeState) { correct++; } else { feedString += String.Format("<li> The word '{0}' is not in the same equivalence class as '{1}' </li>", s.emptyToEpsilon(), representativeString.emptyToEpsilon()); } } feedString += "</ul>"; var grade = 1.0 * int.Parse(maxGrade.Value) * correct / total; if (grade == maxG) { feedString = "Correct!"; } return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>{1}</feedback></div>", (int)(Math.Ceiling(grade)), feedString))); }
/// <summary> /// Finds min edit distance script between DFAs if operation /// takes less than timeout ms /// </summary> /// <param name="dfa1"></param> /// <param name="dfa2"></param> /// <param name="al"></param> /// <param name="solver"></param> /// <param name="timeout"></param> /// <param name="sb"></param> /// <returns></returns> public static DFAEditScript GetDFAOptimalEdit( // copy Automaton <BDD> dfa1, Automaton <BDD> dfa2, HashSet <char> al, CharSetSolver solver, long timeout, StringBuilder sb) { //Contract.Assert(dfa1.IsDeterministic); //Contract.Assert(dfa2.IsDeterministic); DFAEditScript editScript = new DFAEditScript(); #region Add states to dfa2 to make it at least as dfa1 BDD fullAlphabetCondition = BDDOf(al, solver); //Normalize the DFA giving only names from 0 to |States|-1 var normDfaPair = DFAUtilities.normalizeDFA(dfa2); var dfa2augmented = normDfaPair.First; //solver.SaveAsDot(dfa2augmented, "aaaa"); var stateNamesMapping = normDfaPair.Second; //Add states to make dfa2 have the |dfa2.States|>= |dfa1.States| var newMoves = new List <Move <BDD> >(dfa2augmented.GetMoves()); for (int i = 1; i <= dfa1.StateCount - dfa2augmented.StateCount; i++) { int newStateName = dfa2augmented.MaxState + i; //Pick the next available name to be added stateNamesMapping[newStateName] = dfa2.MaxState + i; //save the operation in the script editScript.script.Insert(0, new DFAAddState(dfa2.MaxState + i)); newMoves.Add(new Move <BDD>(newStateName, newStateName, fullAlphabetCondition)); newStateName++; } //Create the new DFA with the added states dfa2augmented = Automaton <BDD> .Create(dfa2augmented.InitialState, dfa2augmented.GetFinalStates().ToList(), newMoves); #endregion int maxScore = (dfa1.StateCount + dfa2augmented.StateCount) * (al.Count + 1); int oldScirptSize = editScript.script.Count; //Start with the internal script equals to null, at the end bestScript.Script will contain the best script DFAEditScript bestScript = new DFAEditScript(); bestScript.script = null; Stopwatch sw = new Stopwatch(); sw.Start(); // Iteratively check if there exists an edit of a given depth for (int depth = 1; true; depth++) { var editList = new List <DFAEdit>(); if (GetDFAEditScriptTimeout( dfa1, dfa2augmented, al, solver, new List <long>(), editScript.script, depth, timeout, sw, DFAUtilities.MyHillTestGeneration(al, dfa1, solver), DFADensity.GetDFADensity(dfa1, al, solver), editScript.GetCost(), bestScript, stateNamesMapping)) { // if hits timeout break and return null break; } if (bestScript.script != null) { bestScript.script.Reverse(); sw.Stop(); return(bestScript); } } sw.Stop(); return(null); }
public static Pair <HashSet <char>, Automaton <BDD> > parseRegexFromXML(XElement regex, XElement alphabet, CharSetSolver solver) { HashSet <char> al = new HashSet <char>(); XElement xmlAlphabet = XElement.Parse(RemoveAllNamespaces(alphabet.ToString())); string alRex = ""; bool first = true; foreach (XElement child in xmlAlphabet.Elements()) { char element = Convert.ToChar(child.Value); al.Add(element); if (first) { first = false; alRex += element; } else { alRex += "|" + element; } } XElement Regex = XElement.Parse(RemoveAllNamespaces(regex.ToString())); string rexpr = Regex.Value.Trim(); var escapedRexpr = string.Format(@"^({0})$", rexpr); Automaton <BDD> aut = null; try { aut = solver.Convert(escapedRexpr).RemoveEpsilons(solver.MkOr).Determinize(solver); } catch (ArgumentException e) { throw new PDLException("The input is not a well formatted regular expression: " + e.Message); } catch (AutomataException e) { throw new PDLException("The input is not a well formatted regular expression: " + e.Message); } var diff = aut.Intersect(solver.Convert(@"^(" + alRex + @")*$").Complement(solver), solver); if (!diff.IsEmpty) { throw new PDLException( "The regular expression should only accept strings over (" + alRex + ")*. Yours accepts the string '" + DFAUtilities.GenerateShortTerm(diff.Determinize(solver), solver) + "'"); } return(new Pair <HashSet <char>, Automaton <BDD> >(al, aut)); }
public override string ToString() { long enumTimeout = 1000L; #region feedback components PDLEnumerator pdlEnumerator = new PDLEnumerator(); PDLPred symmPhi = null; PDLPred underPhi = null; string posWitness = null; string negWitness = null; //If hint or solution try computing the description of the symmdiff if (level == FeedbackLevel.Hint || level == FeedbackLevel.Solution) { //Avoid formulas that are too complex var maxSize = 7; if (symmetricDifference.StateCount < 15) { foreach (var phi1 in pdlEnumerator.SynthesizePDL(alphabet, symmetricDifference, solver, new StringBuilder(), enumTimeout)) { var sizePhi1 = phi1.GetFormulaSize(); if (sizePhi1 < maxSize && !phi1.IsComplex()) { maxSize = sizePhi1; symmPhi = phi1; } } } } //Avoid empty string case and particular string if (symmPhi is PDLEmptyString || symmPhi is PDLIsString) { symmPhi = null; } //If not minimal try computing and underapprox of symmdiff if (symmPhi == null && level != FeedbackLevel.Minimal) { //Avoid formulas that are too complex var minSize = 9; if (symmetricDifference.StateCount < 15) { foreach (var phi2 in pdlEnumerator.SynthesizeUnderapproximationPDL(alphabet, symmetricDifference, solver, new StringBuilder(), enumTimeout)) { var formula = phi2.First; var sizeForm = formula.GetFormulaSize(); if (sizeForm < minSize && !formula.IsComplex()) { minSize = sizeForm; underPhi = formula; } break; } } } //Avoid empty string case and particular string if (underPhi is PDLEmptyString || underPhi is PDLIsString) { underPhi = null; } if (!positiveDifference.IsEmpty) { posWitness = DFAUtilities.GenerateShortTerm(positiveDifference, solver); } else { negWitness = DFAUtilities.GenerateShortTerm(negativeDifference, solver); } #endregion string result = ""; //string.Format("U: {0}%. ", utility); if (symmPhi != null) { if (symmPhi is PDLEmptyString) { result += "Your solution does not behave correctly on the empty string"; } else { result += string.Format("Your solution is not correct on this set of strings: <br /> <div align='center'>{0}</div>", PDLUtil.ToEnglishString(symmPhi)); } } else if (underPhi != null) { if (underPhi is PDLEmptyString) { result += "Your solution does not behave correctly on the empty string"; } else { result += string.Format("Your solution is not correct on this set of strings: <br /> <div align='center'>{0}</div>", PDLUtil.ToEnglishString(underPhi)); } } else { if (posWitness != null) { result += string.Format("Your solution does not accept the {0} while the correct solution does.", posWitness != "" ? "string '<i>" + posWitness + "</i>'" : "empty string"); } else { result += string.Format("Your solution accepts the {0} while the correct solution doesn't.", negWitness != "" ? "string '<i>" + negWitness + "</i>'" : "empty string"); } } return(result); }
public static XElement getSameFeedback(XElement regex, XElement xAlphabet, XElement first, XElement second, XElement notEquivalent, XElement reason, XElement maxGrade) { string firstString = (XElement.Parse(DFAUtilities.RemoveAllNamespaces(first.ToString()))).Value.Trim().decodeEpsilon(); string secondString = (XElement.Parse(DFAUtilities.RemoveAllNamespaces(second.ToString()))).Value.Trim().decodeEpsilon(); string reasonString = (XElement.Parse(DFAUtilities.RemoveAllNamespaces(reason.ToString()))).Value.Trim(); bool areEquivalentAttempt = int.Parse(notEquivalent.Value) == 1 ? false : true; int maxG = int.Parse(maxGrade.Value); HashSet <char> alphabet = parseAlphabet(xAlphabet); CharSetSolver solver = new CharSetSolver(BitWidth.BV64); var dfaPair = DFAUtilities.parseRegexFromXML(regex, xAlphabet, solver); var dfa = dfaPair.Second.Minimize(solver); int firstState = DFAUtilities.GetStateAfterString(dfa.InitialState, firstString, dfa, solver); int secondState = DFAUtilities.GetStateAfterString(dfa.InitialState, secondString, dfa, solver); bool areEquivalent = (firstState == secondState); if (areEquivalent != areEquivalentAttempt) { return(XElement.Parse(string.Format("<div><grade>0</grade><feedback>Wrong equivalency assesment!</feedback></div>"))); } else if (areEquivalent) { try { var suffixDfa = Automaton <BDD> .Create(firstState, dfa.GetFinalStates(), dfa.GetMoves()); var reasonDfaPair = DFAUtilities.parseRegexFromXML(reason, xAlphabet, solver); var dfaGradingFeedback = DFAGrading.GetGrade(suffixDfa, reasonDfaPair.Second, alphabet, solver, 1500, maxG, FeedbackLevel.Minimal, false, false, true); var feedString = "<div>"; foreach (var feed in dfaGradingFeedback.Second) { feedString += string.Format("{0}<br />", feed); } feedString += "</div>"; if (maxG == dfaGradingFeedback.First) { return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>Correct!</feedback></div>", maxG))); } else { return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>{1}</feedback></div>", Math.Max((int)(maxG / 3), dfaGradingFeedback.First), feedString))); } } catch (PDLException pdlex) { return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>Parsing Error: {1}</feedback></div>", (int)(maxG / 3), pdlex.Message))); } } else { // Finding a shortest differentiating word var shortestDiff = DFAUtilities.GetDifferentiatingWord(firstState, secondState, dfa, alphabet, solver).Second; reasonString = reasonString.decodeEpsilon(); int endFirst = DFAUtilities.GetStateAfterString(firstState, reasonString, dfa, solver); int endSecond = DFAUtilities.GetStateAfterString(secondState, reasonString, dfa, solver); bool c1 = dfa.GetFinalStates().Contains(endFirst); bool c2 = dfa.GetFinalStates().Contains(endSecond); if (c1 ^ c2) { return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>Correct!</feedback></div>", maxG))); } else { string feedString = string.Format("The words '{0}' and '{2}' are both {1} by the language", (firstString + reasonString).emptyToEpsilon(), c1 ? "accepted" : "not accepted", (secondString + reasonString).emptyToEpsilon()); return(XElement.Parse(string.Format("<div><grade>{0}</grade><feedback>{1}</feedback></div>", (int)(maxG / 3), feedString))); } } }
public static IEnumerable <Regexp> SynthesizeRegexp(HashSet <char> alphabet, Automaton <BDD> dfa, CharSetSolver s, StringBuilder sb, long timeout) { using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"..\..\..\regexpenum.txt")) { solver = s; numStates = dfa.StateCount; alph = alphabet; #region test variables StringBuilder sb1 = new StringBuilder(); int lim = 0; Stopwatch membershipTimer = new Stopwatch(); Stopwatch equivTimer = new Stopwatch(); timer = new Stopwatch(); timer.Start(); #endregion #region TestSets for equiv var mytests = DFAUtilities.MyHillTestGeneration(alphabet, dfa, solver); var posMN = mytests.First; var negMN = mytests.Second; var tests = DFAUtilities.GetTestSets(dfa, alphabet, solver); var positive = tests.First; var negative = tests.Second; foreach (var t in posMN) { positive.Remove(t); } foreach (var t in negMN) { negative.Remove(t); } #endregion #region Sigma Star bool fst = true; foreach (var c in alph) { if (fst) { fst = false; sigmaStar = new RELabel(c); } else { sigmaStar = new REUnion(sigmaStar, new RELabel(c)); } } sigmaPlus = new REPlus(sigmaStar); sigmaStar = new REStar(sigmaStar); #endregion #region Accessories vars maxWidthC = 0; maxSigmaStarC = 0; var isSubset = true; HashSet <string> visited = new HashSet <string>(); HashSet <string> newReg = new HashSet <string>(); currUnionEls = new Dictionary <string, Automaton <BDD> >(); memoDfa = new Dictionary <string, Automaton <BDD> >(); List <Regexp> subsetReg = new List <Regexp>(); #endregion for (maxWidth = 1; true; maxWidth++) { newReg = new HashSet <string>(); maxSigmaStar = 2; foreach (var regexp in EnumerateRegexp()) { #region run for at most timeout if (timer.ElapsedMilliseconds > timeout) { sb.AppendLine("| Timeout"); timer.Stop(); yield break; } #endregion var re = regexp.Normalize(); if (!(visited.Contains(re.ToString()))) { visited.Add(re.ToString()); sb1 = new StringBuilder(); sb1.Append(re.ToString()); file.WriteLine(sb1); lim++; #region Membership test membershipTimer.Start(); isSubset = CorrectOnNegSet(regexp, negMN); membershipTimer.Stop(); #endregion #region equivalence check if (isSubset) { membershipTimer.Start(); if (CorrectOnNegSet(regexp, negative)) { if (CorrectOnPosSet(regexp, posMN) && CorrectOnPosSet(regexp, positive)) { membershipTimer.Stop(); equivTimer.Start(); var rDfa = getDfa(regexp); memoDfa[regexp.ToString()] = rDfa; if (rDfa.IsEquivalentWith(dfa, solver)) { isSubset = false; equivTimer.Stop(); timer.Stop(); sb.Append("| "); regexp.ToString(sb); sb.AppendLine("|"); sb.AppendLine(string.Format("| elapsed time: \t {0} ms", timer.ElapsedMilliseconds)); sb.AppendLine(string.Format("| equivalence cost:\t {0} ms", equivTimer.ElapsedMilliseconds)); sb.AppendLine(string.Format("| membership cost: \t {0} ms", membershipTimer.ElapsedMilliseconds)); sb.AppendLine(string.Format("| attempts: \t {0}", lim)); yield return(regexp); } else { Console.WriteLine("used dfa"); equivTimer.Stop(); } } else { membershipTimer.Stop(); } } else { membershipTimer.Stop(); isSubset = false; } } #endregion //#region Subsets //if (isSubset) //{ // foreach (var reg1 in subsetReg) // { // var union = (reg1.CompareTo(regexp) > 0) ? (new REUnion(reg1, regexp)) : (new REUnion(regexp, reg1)); // visited.Add(union.ToString()); // sb1 = new StringBuilder(); // sb1.Append(union + " From union"); // file.WriteLine(sb1); // lim++; // membershipTimer.Start(); // if (CorrectOnPosSet(union, posMN) && CorrectOnPosSet(union, positive)) // { // membershipTimer.Stop(); // equivTimer.Start(); // var rDfa = getDfa(union); // memoDfa[union.ToString()] = rDfa; // if (rDfa.IsEquivalentWith(dfa, solver)) // { // equivTimer.Stop(); // timer.Stop(); // sb.Append("| "); // union.ToString(sb); // sb.AppendLine("|"); // sb.AppendLine(string.Format("| elapsed time: \t {0} ms", timer.ElapsedMilliseconds)); // sb.AppendLine(string.Format("| equivalence cost:\t {0} ms", equivTimer.ElapsedMilliseconds)); // sb.AppendLine(string.Format("| membership cost: \t {0} ms", membershipTimer.ElapsedMilliseconds)); // sb.AppendLine(string.Format("| attempts: \t {0}", lim)); // yield return union; // } // else // { // Console.WriteLine("used dfa"); // equivTimer.Stop(); // } // } // else // { // membershipTimer.Stop(); // } // } // subsetReg.Add(regexp); //} //#endregion } } visited = new HashSet <string>(visited.Union(newReg)); } } }
// looks for an edit at depth "depth" // returns false and null in bestScript if no edit is found at depth "depth" // returns false and not null in bestScript if found // returns true if timeout internal static bool GetDFAEditScriptTimeout( Automaton <BDD> dfa1, Automaton <BDD> dfa2, HashSet <char> al, CharSetSolver solver, List <long> editScriptHash, List <DFAEdit> editList, int depth, long timeout, Stopwatch sw, Pair <IEnumerable <string>, IEnumerable <string> > tests, double dfa1density, int totalCost, DFAEditScript bestScript, Dictionary <int, int> stateNamesMapping) { // check timer if (sw.ElapsedMilliseconds > timeout) { return(true); } //Compute worst case distance, call finalScript with this value? int dist = (dfa1.StateCount + dfa2.StateCount) * (al.Count + 1); //Stop if no more moves left if (depth == 0) { if (DFAUtilities.ApproximateMNEquivalent(tests, dfa1density, dfa2, al, solver) && dfa2.IsEquivalentWith(dfa1, solver)) { //check if totalCost < finalScript cost and replace if needed if (bestScript.script == null || totalCost < bestScript.GetCost()) { bestScript.script = ObjectCopier.Clone <List <DFAEdit> >(editList); } } return(false); } DFAEdit edit = null; #region Flip one move target state foreach (var move in dfa2.GetMoves()) { //Creaty copy of the moves without current move var movesWithoutCurrMove = dfa2.GetMoves().ToList(); movesWithoutCurrMove.Remove(move); //Redirect every ch belonging to move condition foreach (var c in solver.GenerateAllCharacters(move.Label, false)) { long hash = IntegerUtil.PairToInt(move.SourceState, c - 97) + dfa2.StateCount; if (CanAdd(hash, editScriptHash)) { editScriptHash.Insert(0, hash); //Local copy of moves var newMoves = movesWithoutCurrMove.ToList(); var newMoveCondition = solver.MkCharConstraint(false, c); #region Remove ch from current move var andCond = solver.MkAnd(move.Label, solver.MkNot(newMoveCondition)); //add back move without ch iff satisfiable if (solver.IsSatisfiable(andCond)) { newMoves.Add(new Move <BDD>(move.SourceState, move.TargetState, andCond)); } #endregion #region Redirect c to a different state foreach (var state in dfa2.States) { if (state != move.TargetState) { var newMovesComplete = newMoves.ToList(); newMovesComplete.Add(new Move <BDD>(move.SourceState, state, newMoveCondition)); var dfa2new = Automaton <BDD> .Create(dfa2.InitialState, dfa2.GetFinalStates(), newMovesComplete); edit = new DFAEditMove(stateNamesMapping[move.SourceState], stateNamesMapping[state], c); editList.Insert(0, edit); if (GetDFAEditScriptTimeout(dfa1, dfa2new, al, solver, editScriptHash, editList, depth - 1, timeout, sw, tests, dfa1density, totalCost + edit.GetCost(), bestScript, stateNamesMapping)) { return(true); } editList.RemoveAt(0); } } #endregion editScriptHash.RemoveAt(0); } } } #endregion #region Flip one state from fin to non fin foreach (var state in dfa2.States) { if (CanAdd(state, editScriptHash)) { //flip its final non final status editScriptHash.Insert(0, state); var newFinalStates = new HashSet <int>(dfa2.GetFinalStates()); Automaton <BDD> dfa2new = null; if (dfa2.GetFinalStates().Contains(state)) { edit = new DFAEditState(stateNamesMapping[state], false); editList.Insert(0, edit); newFinalStates.Remove(state); dfa2new = Automaton <BDD> .Create(dfa2.InitialState, newFinalStates, dfa2.GetMoves()); } else { edit = new DFAEditState(stateNamesMapping[state], true); editList.Insert(0, edit); newFinalStates.Add(state); dfa2new = Automaton <BDD> .Create(dfa2.InitialState, newFinalStates, dfa2.GetMoves()); } if (GetDFAEditScriptTimeout(dfa1, dfa2new, al, solver, editScriptHash, editList, depth - 1, timeout, sw, tests, dfa1density, totalCost + edit.GetCost(), bestScript, stateNamesMapping)) { return(true); } editScriptHash.RemoveAt(0); editList.RemoveAt(0); } } #endregion return(false); }