/// <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); }
/// <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 NFANotMinimalFeedback( FeedbackLevel level, HashSet <char> alphabet, int stateDiff, int transitionDiff, NFAEditScript script, CharSetSolver solver) : base(level, alphabet, solver) { this.transitionDiff = transitionDiff; this.stateDiff = stateDiff; this.script = script; }
public NFANotMinimalFeedback ( FeedbackLevel level, HashSet<char> alphabet, int stateDiff, int transitionDiff, NFAEditScript script, CharSetSolver solver) : base(level, alphabet, solver) { this.transitionDiff = transitionDiff; this.stateDiff = stateDiff; this.script = script; }
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; }
/// <summary> /// Computes the grade for attempt using all the possible metrics /// </summary> /// <param name="solutionNFA">correct nfa</param> /// <param name="attemptNFA">nfa to be graded</param> /// <param name="alpahbet">input alphabet</param> /// <param name="solver">SMT solver for char set</param> /// <param name="timeout">timeout for the PDL enumeration (suggested > 1000)</param> /// <param name="maxGrade">Max grade for the homework</param> /// <param name="level">Feedback level</param> /// <returns>Grade for nfa2</returns> public static Pair <int, IEnumerable <NFAFeedback> > GetGrade( Automaton <BDD> solutionNFA, Automaton <BDD> attemptNFA, HashSet <char> alphabet, CharSetSolver solver, long timeout, int maxGrade, FeedbackLevel level) { var feedbacks = new List <NFAFeedback>(); int deadStateDeduction = 0; int tooBigDeduction = 0; int incorrectDeduction = 0; // Remove at most a percentage of max grade when NFA is big double maxDeductionForTooBig = ((double)maxGrade * 0.3); double maxDeductionForDeadStates = ((double)maxGrade * 0.1); double solutionStateCount = solutionNFA.StateCount; double attemptStateCount = attemptNFA.StateCount; double solutionTransCount = solutionNFA.MoveCount; double attemptTransCount = attemptNFA.MoveCount; NFAEditDistanceProvider nfaedp = new NFAEditDistanceProvider(solutionNFA, alphabet, solver, timeout); //Check if using epsilon and nondeterminism if (solutionNFA.IsEpsilonFree) { solutionNFA.CheckDeterminism(solver); } if (attemptNFA.IsEpsilonFree) { attemptNFA.CheckDeterminism(solver); } bool shouldUseEpsilon = !solutionNFA.IsEpsilonFree && attemptNFA.IsEpsilonFree; bool shouldUseNonDet = !shouldUseEpsilon && !solutionNFA.isDeterministic && attemptNFA.isDeterministic; //Check if solution has dead states and remove if it does var statesBeforeDeadStatesElimination = attemptNFA.StateCount; attemptNFA.EliminateDeadStates(); var solutionHasDeadStates = attemptNFA.StateCount < statesBeforeDeadStatesElimination; //Start checking equiv bool areEquivalent = solutionNFA.IsEquivalentWith(attemptNFA, solver); if (areEquivalent) { // prompt nfa is correct feedbacks.Insert(0, new NFAStringFeedback(level, alphabet, solver, "Your NFA accepts the CORRECT language.")); #region Check number of states and decrease grade if too big int stateDiff = (int)(attemptStateCount - solutionStateCount); int transDiff = (int)(attemptTransCount - solutionTransCount); //If is not minimal apply deduction and compute edit if (stateDiff > 0 || transDiff > 0) { #region Try to collapse for feedback // Try to find a way to collaps states or remove states and transitions to make the NFA smaller NFAEditScript collapseScript = null; var edit = nfaedp.NFACollapseSearch(attemptNFA); if (edit != null) { collapseScript = new NFAEditScript(); collapseScript.script.Insert(0, edit); } feedbacks.Add(new NFANotMinimalFeedback(level, alphabet, stateDiff, transDiff, collapseScript, solver)); #endregion #region Compute tooBigDeduction if (stateDiff > 0) { // ((att/sol)^2)-1 var stateRatio = attemptStateCount / solutionStateCount; var stateRatioSqM1 = Math.Pow(stateRatio, 2) - 1; var sclaedStateRatio = stateRatioSqM1 * maxDeductionForTooBig / 2; tooBigDeduction = (int)Math.Round(Math.Min(sclaedStateRatio, maxDeductionForTooBig)); } else { if (transDiff > 0) { // ((att/sol)^2)-1 var transRatio = attemptTransCount / solutionTransCount; var transRatioSqM1 = Math.Pow(transRatio, 2) - 1; var sclaedTransRatio = transRatioSqM1 * maxDeductionForTooBig / 2; tooBigDeduction = (int)Math.Round(Math.Min(sclaedTransRatio, maxDeductionForTooBig)); } } //Make sure deduction is positive tooBigDeduction = Math.Max(tooBigDeduction, 0); #endregion } #endregion } else { // prompt nfa is incorrect feedbacks.Add(new NFAStringFeedback(level, alphabet, solver, "Your NFA does NOT accept the correct langauge.")); //inequivalent, try using grading metrics and based on winning metric give feedback int remainingGrade = maxGrade - tooBigDeduction; #region metric computation //compute deterministic versions var solutionNFAdet = solutionNFA.RemoveEpsilons(solver.MkOr).Determinize(solver).MakeTotal(solver).Minimize(solver); var attemptNFAdet = attemptNFA.RemoveEpsilons(solver.MkOr).Determinize(solver).MakeTotal(solver).Minimize(solver); solutionNFAdet.EliminateDeadStates(); attemptNFAdet.EliminateDeadStates(); //compute density double densityRatio = DFADensity.GetDFADifferenceRatio(solutionNFAdet, attemptNFAdet, alphabet, solver); //compute edit distance double nfaED = 2; var editScript = nfaedp.GetNFAOptimalEdit(attemptNFA); if (editScript != null) { nfaED = ((double)(editScript.GetCost())) / ((double)((solutionNFA.StateCount + 1) * alphabet.Count)); } #endregion #region metrics scaling var scalingSquareDensity = 1; var multv2 = 0.5; var scalingSquareDFAED = 1.03; var scaledDensityRatio = (scalingSquareDensity + (multv2 * densityRatio)) * (scalingSquareDensity + (multv2 * densityRatio)) - scalingSquareDensity * scalingSquareDensity; var scaledNfaED = (scalingSquareDFAED + nfaED) * (scalingSquareDFAED + nfaED) - scalingSquareDFAED * scalingSquareDFAED; //If the edit script was not computed make sure it loses. if (editScript == null) { scaledNfaED = Double.MaxValue; } //Select dominating Feedback based on grade double unscaledGrade = Math.Min(scaledDensityRatio, scaledNfaED); var dfaedwins = scaledNfaED <= scaledDensityRatio; var densitywins = scaledDensityRatio <= scaledNfaED; incorrectDeduction = (int)Math.Round(unscaledGrade * (double)(maxGrade)); #endregion //If edit distance search works, provides feedback based upon result //Otherwise, gives counterexample feedback if (level != FeedbackLevel.Minimal) { if (dfaedwins) { feedbacks.Add(new NFAEDFeedback(solutionNFA, attemptNFA, level, alphabet, editScript, solver)); } else { feedbacks.Add(new NFACounterexampleFeedback(level, alphabet, solutionNFAdet, attemptNFAdet, solver)); } } } // Feedback related to nondeterminism and epislon if (shouldUseEpsilon) { feedbacks.Add(new NFAStringFeedback(level, alphabet, solver, "You should try using epsilon transitions.")); } if (shouldUseNonDet) { feedbacks.Add(new NFAStringFeedback(level, alphabet, solver, "You should try using nondeterminism.")); } // Deduct points and prompt feedback is solution has dead states if (solutionHasDeadStates) { deadStateDeduction = (int)maxDeductionForDeadStates; feedbacks.Add(new NFAStringFeedback(level, alphabet, solver, "Your NFA has some dead states.")); } //Grade computation //changed to be binary, because we did not tell them of the deductions //int grade = Math.Max(maxGrade - deadStateDeduction - tooBigDeduction - incorrectDeduction, 0); //int grade = areEquivalent ? maxGrade : 0; // This requires further testing, but appears to grade ok, 0 points for empty automaton and full points for something in between int grade = Math.Max(maxGrade - incorrectDeduction, 0); return(new Pair <int, IEnumerable <NFAFeedback> >(grade, feedbacks)); }
// 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 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; }
// 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); }
/// <summary> /// Computes the grade for attempt using all the possible metrics /// </summary> /// <param name="solutionNFA">correct nfa</param> /// <param name="attemptNFA">nfa to be graded</param> /// <param name="alpahbet">input alphabet</param> /// <param name="solver">SMT solver for char set</param> /// <param name="timeout">timeout for the PDL enumeration (suggested > 1000)</param> /// <param name="maxGrade">Max grade for the homework</param> /// <param name="level">Feedback level</param> /// <returns>Grade for nfa2</returns> public static Pair<int, IEnumerable<NFAFeedback>> GetGrade( Automaton<BDD> solutionNFA, Automaton<BDD> attemptNFA, HashSet<char> alphabet, CharSetSolver solver, long timeout, int maxGrade, FeedbackLevel level) { var feedbacks = new List<NFAFeedback>(); int deadStateDeduction = 0; int tooBigDeduction = 0; int incorrectDeduction = 0; // Remove at most a percentage of max grade when NFA is big double maxDeductionForTooBig = ((double)maxGrade * 0.3); double maxDeductionForDeadStates = ((double)maxGrade * 0.1); double solutionStateCount = solutionNFA.StateCount; double attemptStateCount = attemptNFA.StateCount; double solutionTransCount = solutionNFA.MoveCount; double attemptTransCount = attemptNFA.MoveCount; NFAEditDistanceProvider nfaedp = new NFAEditDistanceProvider(solutionNFA, alphabet, solver, timeout); //Check if using epsilon and nondeterminism if (solutionNFA.IsEpsilonFree) solutionNFA.CheckDeterminism(solver); if (attemptNFA.IsEpsilonFree) attemptNFA.CheckDeterminism(solver); bool shouldUseEpsilon = !solutionNFA.IsEpsilonFree && attemptNFA.IsEpsilonFree; bool shouldUseNonDet = !shouldUseEpsilon && !solutionNFA.isDeterministic && attemptNFA.isDeterministic; //Check if solution has dead states and remove if it does var statesBeforeDeadStatesElimination = attemptNFA.StateCount; attemptNFA.EliminateDeadStates(); var solutionHasDeadStates = attemptNFA.StateCount < statesBeforeDeadStatesElimination; //Start checking equiv bool areEquivalent = solutionNFA.IsEquivalentWith(attemptNFA, solver); if (areEquivalent) { // prompt nfa is correct feedbacks.Insert(0, new NFAStringFeedback(level, alphabet, solver, "Your NFA accepts the CORRECT language.")); #region Check number of states and decrease grade if too big int stateDiff = (int)(attemptStateCount - solutionStateCount); int transDiff = (int)(attemptTransCount - solutionTransCount); //If is not minimal apply deduction and compute edit if (stateDiff > 0 || transDiff > 0) { #region Try to collapse for feedback // Try to find a way to collaps states or remove states and transitions to make the NFA smaller NFAEditScript collapseScript = null; var edit = nfaedp.NFACollapseSearch(attemptNFA); if (edit != null) { collapseScript = new NFAEditScript(); collapseScript.script.Insert(0, edit); } feedbacks.Add(new NFANotMinimalFeedback(level, alphabet, stateDiff, transDiff, collapseScript, solver)); #endregion #region Compute tooBigDeduction if (stateDiff > 0) { // ((att/sol)^2)-1 var stateRatio = attemptStateCount / solutionStateCount; var stateRatioSqM1 = Math.Pow(stateRatio, 2) - 1; var sclaedStateRatio = stateRatioSqM1 * maxDeductionForTooBig / 2; tooBigDeduction = (int)Math.Round(Math.Min(sclaedStateRatio, maxDeductionForTooBig)); } else { if (transDiff > 0) { // ((att/sol)^2)-1 var transRatio = attemptTransCount / solutionTransCount; var transRatioSqM1 = Math.Pow(transRatio, 2) - 1; var sclaedTransRatio = transRatioSqM1 * maxDeductionForTooBig / 2; tooBigDeduction = (int)Math.Round(Math.Min(sclaedTransRatio, maxDeductionForTooBig)); } } //Make sure deduction is positive tooBigDeduction = Math.Max(tooBigDeduction, 0); #endregion } #endregion } else { // prompt nfa is incorrect feedbacks.Add(new NFAStringFeedback(level, alphabet, solver, "Your NFA does NOT accept the correct langauge.")); //inequivalent, try using grading metrics and based on winning metric give feedback int remainingGrade = maxGrade - tooBigDeduction; #region metric computation //compute deterministic versions var solutionNFAdet = solutionNFA.RemoveEpsilons(solver.MkOr).Determinize(solver).MakeTotal(solver).Minimize(solver); var attemptNFAdet = attemptNFA.RemoveEpsilons(solver.MkOr).Determinize(solver).MakeTotal(solver).Minimize(solver); solutionNFAdet.EliminateDeadStates(); attemptNFAdet.EliminateDeadStates(); //compute density double densityRatio = DFADensity.GetDFADifferenceRatio(solutionNFAdet, attemptNFAdet, alphabet, solver); //compute edit distance double nfaED = 2; var editScript = nfaedp.GetNFAOptimalEdit(attemptNFA); if (editScript != null) nfaED = ((double)(editScript.GetCost())) / ((double)((solutionNFA.StateCount + 1) * alphabet.Count)); #endregion #region metrics scaling var scalingSquareDensity = 1; var multv2 = 0.5; var scalingSquareDFAED = 1.03; var scaledDensityRatio = (scalingSquareDensity + (multv2 * densityRatio)) * (scalingSquareDensity + (multv2 * densityRatio)) - scalingSquareDensity * scalingSquareDensity; var scaledNfaED = (scalingSquareDFAED + nfaED) * (scalingSquareDFAED + nfaED) - scalingSquareDFAED * scalingSquareDFAED; //If the edit script was not computed make sure it loses. if (editScript == null) scaledNfaED = Double.MaxValue; //Select dominating Feedback based on grade double unscaledGrade = Math.Min(scaledDensityRatio, scaledNfaED); var dfaedwins = scaledNfaED <= scaledDensityRatio; var densitywins = scaledDensityRatio <= scaledNfaED; incorrectDeduction = (int)Math.Round(unscaledGrade * (double)(maxGrade)); #endregion //If edit distance search works, provides feedback based upon result //Otherwise, gives counterexample feedback if (level != FeedbackLevel.Minimal) { if (dfaedwins) feedbacks.Add(new NFAEDFeedback(solutionNFA, attemptNFA, level, alphabet, editScript, solver)); else feedbacks.Add(new NFACounterexampleFeedback(level, alphabet, solutionNFAdet, attemptNFAdet, solver)); } } // Feedback related to nondeterminism and epislon if (shouldUseEpsilon) feedbacks.Add(new NFAStringFeedback(level, alphabet, solver, "You should try using epsilon transitions.")); if (shouldUseNonDet) feedbacks.Add(new NFAStringFeedback(level, alphabet, solver, "You should try using nondeterminism.")); // Deduct points and prompt feedback is solution has dead states if (solutionHasDeadStates) { deadStateDeduction = (int)maxDeductionForDeadStates; feedbacks.Add(new NFAStringFeedback(level, alphabet, solver, "Your NFA has some dead states.")); } //Grade computation int grade = Math.Max(maxGrade - deadStateDeduction - tooBigDeduction - incorrectDeduction, 0); return new Pair<int, IEnumerable<NFAFeedback>>(grade, feedbacks); }