// Strategy: // a) Match the prefix and suffix of the language with the x and z // b) See if y can be pumped against the middle part // The horrifying part is the quantifiers public static bool splitGood(Split split, ArithmeticLanguage language, BooleanExpression additionalConstraint) { // We just need to find a k such that (x y^k z) \not\in L // If we cannot find such a k for some p, it is a bad split // Say i, j are the variables in the language constraint and // v_1, v_2 are variables in split constraints // Therefore, if for all p, v_1, \ldots, v_n that satisfies split constraints // and additional constraints and exists k such that for all i, j language // constraint does not hold, then split is bad Console.WriteLine("\t\tSplit is good if none of the following mids can be pumped: "); // We will definitely go through the loop at least once as match is working var beginningMatches = Matcher .match(split.start, language.symbolic_string) .Where(x => !x.FirstRemaining) .Select(x => x.withAdditionalConstraint(language.constraint)) .Select(x => x.withAdditionalConstraint(additionalConstraint)) .Where(x => x.isFeasible()); foreach (var beginMatch in beginningMatches) { var remainingLanguage = beginMatch.remaining2; var endMatches = Matcher .match(split.end.reverse(), remainingLanguage.reverse()) .Where(x => !x.FirstRemaining) .Select(x => x.withAdditionalConstraint(language.constraint)) .Select(x => x.withAdditionalConstraint(additionalConstraint)) .Where(x => x.isFeasible()); foreach (var endMatch in endMatches) { var fullConstraint = LogicalExpression.And(beginMatch.constraint, endMatch.constraint); if (!fullConstraint.isSatisfiable()) { continue; } var midLanguage = endMatch.remaining2.reverse(); var midSplit = split.mid; var ctx = new Context(); // Console.WriteLine("\t\t" + midLanguage + " ===== " + midSplit + " when " + fullConstraint); // var z3exp = fullConstraint.toZ3(ctx).Simplify(); // Console.WriteLine("\t\t" + midLanguage + " ===== " + midSplit + " when " + z3exp); if (!canMismatchOne(midLanguage, midSplit, fullConstraint)) { return(false); } } } return(true); }
public static bool NonRegularCheckI(ArithmeticLanguage language, string start, string mid, string end, int i) { string fullWord = pumpMid(start, mid, end, i); if (language.symbolic_string.isFlat()) { SymbolicString wordSS = Parser.parseSymbolicString(fullWord.ToString(), language.alphabet.ToList()); return(ProofChecker.checkContainment(wordSS, language, LogicalExpression.True())); } else { throw new PumpingLemmaException("Arithmetic Language must be flat!"); } }
public static bool check(ArithmeticLanguage language, SymbolicString pumpingString) { if (pumpingString.GetIntegerVariables().Count() != 1) { return(false); } var pumpingLength = pumpingString.GetIntegerVariables().First(); var pumpingLengthVariable = PumpingLemma.LinearIntegerExpression .SingleTerm(1, pumpingLength); var additionalConstraint = LogicalExpression.And( PumpingLemma.ComparisonExpression.GreaterThan(pumpingLengthVariable, 0), LogicalExpression.And(pumpingString.repeats().Select(x => ComparisonExpression.GreaterThanOrEqual(x, 0))) ); // 0. Need to check if pumping string grows unboundedly with p var pumpingStringLength = pumpingString.length(); if (pumpingStringLength.isConstant() || pumpingStringLength.coefficients[pumpingLength] < 0) { return(false); } foreach (var r in pumpingString.repeats()) { if (r.coefficients[pumpingLength] < 0) { return(false); } } // 1. Check that the pumping string is in the language for all p if (!checkContainment(pumpingString, language, additionalConstraint)) { return(false); } Console.WriteLine("Language is non-regular if all the following splits are good:"); int i = 0; // 2. Check that each split of the pumping string has an valid pumping length foreach (var split in pumpingString.ValidSplits(pumpingLengthVariable, additionalConstraint)) { Console.WriteLine("\t" + (i++) + ": " + split + " when " + additionalConstraint); if (!splitGood(split, language, additionalConstraint)) { return(false); } } return(true); }
private static IEnumerable <Match> matchConcatConcat(SymbolicString s1, SymbolicString s2) { if (s1.isEpsilon() || s2.isEpsilon()) { yield return(Match.MakeMatch(LogicalExpression.True(), s1, s2)); yield break; } // Quick match prefix symbols int i; for (i = 0; i < s1.sub_strings.Count && i < s2.sub_strings.Count; i++) { if (s1.sub_strings[i].expression_type != SymbolicString.SymbolicStringType.Symbol || s2.sub_strings[i].expression_type != SymbolicString.SymbolicStringType.Symbol) { break; } if (s1.sub_strings[i].atomic_symbol != s2.sub_strings[i].atomic_symbol) { yield break; } } var sub1 = s1.sub_strings.Skip(i); var sub2 = s2.sub_strings.Skip(i); // If we have consumed something fully, just return the remaining bits if (i == s1.sub_strings.Count || i == s2.sub_strings.Count) { yield return(Match.MakeMatch( LogicalExpression.True(), SymbolicString.Concat(sub1), SymbolicString.Concat(sub2))); yield break; } var rest1 = SymbolicString.Concat(sub1.Skip(1)); var rest2 = SymbolicString.Concat(sub2.Skip(1)); foreach (var m in match(sub1.First(), sub2.First())) { foreach (var mp in m.continueMatch(rest1, rest2)) { yield return(mp); } } }
public static string NonRegularGetUnpumpableWord(ArithmeticLanguage language, SymbolicString unpumpableWord, int n) { Dictionary <VariableType, int> assignment = new Dictionary <VariableType, int>(); assignment.Add(VariableType.Variable("n"), n); string word = pumpedWord(unpumpableWord, assignment); SymbolicString ss = SymbolicString.FromTextDescription(language.alphabet.ToList(), word); while (!ProofChecker.checkContainment(ss, language, LogicalExpression.True())) { assignment[VariableType.Variable("n")]++; word = pumpedWord(unpumpableWord, assignment); ss = SymbolicString.FromTextDescription(language.alphabet.ToList(), word); } return(word); }
private static IEnumerable <Match> matchSymbolConcat(SymbolicString s1, SymbolicString s2) { if (s2.isEpsilon()) { yield return(Match.PartialFirst(LogicalExpression.True(), s1)); yield break; } var rest = SymbolicString.Concat(s2.sub_strings.Skip(1)); foreach (var m in match(s1, s2.sub_strings.First())) { foreach (var mp in m.continueMatch(SymbolicString.Epsilon(), rest)) { yield return(mp); } } }
// Either consume symbol with first symbol of repeat and say repeat is positive // Or don't consume symbol and say repeat is zero private static IEnumerable <Match> matchSymbolRepeat(SymbolicString s1, SymbolicString s2) { Debug.Assert(s2.isFlat()); // When repeat is 0 yield return(Match.PartialFirst(ComparisonExpression.Equal(s2.repeat, 0), s1)); // When repeat is non-zero foreach (var m in match(s1, s2.root)) { // Because the root is a word, we immediately know if the symbol matches or not // and get an m if and only if the symbol matches Debug.Assert(!m.FirstRemaining); yield return(Match.PartialSecond( LogicalExpression.And(m.constraint, ComparisonExpression.GreaterThan(s2.repeat, 0)), SymbolicString.Concat(m.remaining2, SymbolicString.Repeat(s2.root, s2.repeat - 1)) )); } }
private static IEnumerable <Match> shortLeftMatches(SymbolicString s1, SymbolicString s2, int firstMismatch) { int l1 = s1.root.wordLength(); for (int i = 1; l1 *i <= firstMismatch; i++) { var s1r = SymbolicString.Concat(Enumerable.Repeat(s1.root, i)); foreach (var m in match(s1r, s2)) { if (m.FirstRemaining) { continue; } yield return(m.withAdditionalConstraint(LogicalExpression.And( ComparisonExpression.Equal(s1.length(), s1r.length()), ComparisonExpression.GreaterThan(m.remaining2.length(), 0) ))); } } }
public Match forceFinish() { BooleanExpression additionalConstraint = LogicalExpression.True(); if (FirstRemaining) { additionalConstraint = LogicalExpression.And( additionalConstraint, ComparisonExpression.Equal(remaining1.length(), 0) ); } if (SecondRemaining) { additionalConstraint = LogicalExpression.And( additionalConstraint, ComparisonExpression.Equal(remaining2.length(), 0) ); } return(FullMatch(LogicalExpression.And(this.constraint, additionalConstraint))); }
// Generates splits (x, y, z) such that |y| > 0 and |xy| < p public IEnumerable <Split> ValidSplits( LinearIntegerExpression pumpingLengthVariable, BooleanExpression additionalConstraints) { foreach (var split in this.Splits()) { if (split.mid.isEpsilon()) { continue; } var cond1 = ComparisonExpression.GreaterThan(split.mid.length(), LinearIntegerExpression.Constant(0)); var cond2 = ComparisonExpression.LessThan(split.start.length() + split.mid.length(), pumpingLengthVariable); var condition = LogicalExpression.And(additionalConstraints, cond1, cond2); if (condition.isMaybeSatisfiable()) { split.AddConstraint(condition); yield return(split); } } }
public static int NonRegularGetI(ArithmeticLanguage language, string start, string mid, string end) { SymbolicString matchingString = splitToSymbStr(language.alphabet.ToList(), start, mid, end); if (ProofChecker.checkContainment(matchingString, language, LogicalExpression.True())) { return(1); // AI surrenders } else { int i = 0; do { string word = pumpMid(start, mid, end, i); SymbolicString ss = SymbolicString.FromTextDescription(language.alphabet.ToList(), word); if (!ProofChecker.checkContainment(ss, language, LogicalExpression.True())) { return(i); } i++; } while (i < 99); //for debugging purposes return(-1); } }
public void AddConstraint(BooleanExpression c) { this.constraints = LogicalExpression.And(this.constraints, c); }
public static BooleanExpression And(IEnumerable <BooleanExpression> ops) { return(ops.Aggregate(LogicalExpression.True() as BooleanExpression, (x, y) => And(x, y))); }
// Generate TwoSplit's where either one may be empty // Generates the splits in order, i.e., by length of the prefix // So, for s being symbol and concat, first one will always be (\epsilon, s) and last one always (s, \epsilon) // So, for s being repeat (abc)^n, first will be ((abc)^i,(abc)^j) and last one always ((abc)^i ab, c(abc)^j) // Requires a flat symbolic string public IEnumerable <TwoSplit> TwoSplits() { SymbolicString prefix, suffix; // Contract.Requires(this.isFlat()); switch (this.expression_type) { case SymbolicStringType.Symbol: yield return(TwoSplit.MakeSplit(SymbolicString.Epsilon(), this, LogicalExpression.True())); yield return(TwoSplit.MakeSplit(this, SymbolicString.Epsilon(), LogicalExpression.True())); break; case SymbolicStringType.Concat: yield return(TwoSplit.MakeSplit(SymbolicString.Epsilon(), this, LogicalExpression.True())); for (int i = 0; i < this.sub_strings.Count; i++) { prefix = SymbolicString.Concat(this.sub_strings.Take(i).ToList()); suffix = SymbolicString.Concat(this.sub_strings.Skip(i + 1).ToList()); foreach (var split in this.sub_strings[i].TwoSplits()) { if (!split.start.isEpsilon()) { yield return(split.extend(prefix, suffix)); } } } break; case SymbolicStringType.Repeat: var v1 = LinearIntegerExpression.FreshVariable(); var v2 = LinearIntegerExpression.FreshVariable(); var sanityConstraint = LogicalExpression.And( ComparisonExpression.GreaterThanOrEqual(v1, 0), ComparisonExpression.GreaterThanOrEqual(v2, 0) ); prefix = SymbolicString.Repeat(this.root, v1); suffix = SymbolicString.Repeat(this.root, v2); yield return(TwoSplit.MakeSplit( prefix, suffix, LogicalExpression.And(sanityConstraint, ComparisonExpression.Equal(v1 + v2, this.repeat)) )); if (this.root.expression_type != SymbolicStringType.Concat) { break; } for (int i = 1; i < this.root.sub_strings.Count; i++) { var split = TwoSplit.MakeSplit( SymbolicString.Concat(this.root.sub_strings.Take(i)), SymbolicString.Concat(this.root.sub_strings.Skip(i)), LogicalExpression.And( ComparisonExpression.Equal(v1 + v2 + 1, this.repeat), sanityConstraint ) ); yield return(split.extend(prefix, suffix)); } break; default: throw new ArgumentOutOfRangeException(); } }
// Generates splits where all three parts may be empty public IEnumerable <Split> Splits() { // Contract.Requires<ArgumentException>(this.isFlat()); switch (this.expression_type) { case SymbolicStringType.Symbol: yield return(Split.MakeSplit(this, SymbolicString.Epsilon(), SymbolicString.Epsilon(), LogicalExpression.True())); yield return(Split.MakeSplit(SymbolicString.Epsilon(), this, SymbolicString.Epsilon(), LogicalExpression.True())); yield return(Split.MakeSplit(SymbolicString.Epsilon(), SymbolicString.Epsilon(), this, LogicalExpression.True())); break; case SymbolicStringType.Repeat: var v1 = LinearIntegerExpression.FreshVariable(); var v2 = LinearIntegerExpression.FreshVariable(); var v3 = LinearIntegerExpression.FreshVariable(); var sanityConstraint = LogicalExpression.And( ComparisonExpression.GreaterThanOrEqual(v1, 0), ComparisonExpression.GreaterThanOrEqual(v2, 0), ComparisonExpression.GreaterThanOrEqual(v3, 0) ); // Suppose the word w = a_0 a_1 \ldots a_{n-1} // All splits are of the form // BEG: (a_0 \ldots a_{n-1})^i (a_0 \ldots a_p) = w^i . w_1 // MID: (a_{p+1} \ldots a_{n-1} a_0 \ldots a_p)^j (a_{p+1} \ldots a_q) = (w_2 w_1)^j w_3 // END: (a_{q+1} \ldots a_{n-1}) (a_0 \ldots a_{n-1})^k = w_4 w^k var w = this.root.wordSymbols(); var ww = w.Concat(w); int n = w.Count(); for (int w1len = 0; w1len < n; w1len++) { var w_1 = w.Take(w1len); var w_2 = w.Skip(w1len); var beg = SymbolicString.Concat( SymbolicString.Repeat(this.root, v1), SymbolicString.Concat(w_1)); var mid_root = SymbolicString.Concat( SymbolicString.Concat(w_2), SymbolicString.Concat(w_1)); var mid_beg = SymbolicString.Repeat(mid_root, v2); for (int w3len = 0; w3len < n; w3len++) { var w_3 = ww.Skip(w1len).Take(w3len); var mid = SymbolicString.Concat(mid_beg, SymbolicString.Concat(w_3.ToList())); IEnumerable <SymbolicString> w_4; if (w1len + w3len == 0) { w_4 = new List <SymbolicString>(); } else if (w1len + w3len <= n) { w_4 = w.Skip(w1len).Skip(w3len); } else { w_4 = ww.Skip(w1len).Skip(w3len); } var end = SymbolicString.Concat( SymbolicString.Concat(w_4.ToList()), SymbolicString.Repeat(this.root, v3) ); var consumed = (w_1.Count() + w_3.Count() + w_4.Count()) / w.Count(); yield return(Split.MakeSplit( beg, mid, end, LogicalExpression.And( ComparisonExpression.Equal(v1 + v2 + v3 + consumed, this.repeat), sanityConstraint ) )); } } break; case SymbolicStringType.Concat: foreach (var beg_midend in this.TwoSplits()) { foreach (var mid_end in beg_midend.end.TwoSplits()) { yield return(Split.MakeSplit( beg_midend.start, mid_end.start, mid_end.end, LogicalExpression.And(beg_midend.constraints, mid_end.constraints))); } } break; default: throw new ArgumentOutOfRangeException(); } }
// Generates XML for the public XElement SplitDisplayXML( VariableType pumpingLength, BooleanExpression additionalConstraints) { Contract.Assert(additionalConstraints.GetVariables().Count() <= 1); if (additionalConstraints.GetVariables().Count == 1) { Contract.Assert(additionalConstraints.GetVariables().First().Equals(pumpingLength)); } var pumpingLengthVariable = LinearIntegerExpression.SingleTerm(1, pumpingLength); var repeatLengths = repeats(); // For great and inexplicable reasons. Trust me, it looks better this way. var displayConstraints = LogicalExpression.And(repeatLengths.Select(x => ComparisonExpression.GreaterThanOrEqual(x, LinearIntegerExpression.Constant(5)) )); XElement symbstrings = new XElement("symbstrings"); foreach (var split in this.ValidSplits(pumpingLengthVariable, additionalConstraints)) { var splitRepeatLengths = split.start.repeats(); splitRepeatLengths.AddRange(split.mid.repeats()); splitRepeatLengths.AddRange(split.end.repeats()); // Get models that don't look terrible var splitDisplayConstraints = LogicalExpression.And(splitRepeatLengths.Select(x => ComparisonExpression.GreaterThanOrEqual(x, LinearIntegerExpression.Constant(1)) )); var constraint = LogicalExpression.And(displayConstraints, split.constraints, splitDisplayConstraints); if (!constraint.isSatisfiable()) { continue; } var splitModel = constraint.getModel(); var symbstr = new XElement("symbstr"); var strings = new XElement("strings"); var splits = new XElement("splits"); var constrs = new XElement("constraints"); Func <string, Tuple <int, int, SymbolicString>, XElement> handleSegment = (parentTagName, segment) => { var parent = new XElement(parentTagName); var from = new XElement("from"); from.Value = segment.Item1.ToString(); var to = new XElement("to"); to.Value = segment.Item2.ToString(); var label = new XElement("label"); label.Add(segment.Item3.ToDisplayXML()); parent.Add(from); parent.Add(to); parent.Add(label); return(parent); }; int strIndex = 0; foreach (var segment in getSegments(splitModel, ref strIndex)) { strings.Add(handleSegment("string", segment)); } int splitIndex = 0; foreach (var segment in new[] { split.start, split.mid, split.end }) { splits.Add(handleSegment("split", segment.makeSegment(splitModel, ref splitIndex))); } var constr = new XElement("constraint"); constr.Add(split.constraints.ToDisplayXML()); constrs.Add(constr); symbstr.Add(strings); symbstr.Add(splits); symbstr.Add(constrs); symbstrings.Add(symbstr); } return(symbstrings); }
public static BooleanExpression Implies(BooleanExpression op1, BooleanExpression op2) { return(LogicalExpression.Or(LogicalExpression.Not(op1), op2)); }
public static ArithmeticLanguage FromTextDescriptions(List <String> alphabet, string symbolicStringText, string constraintText) { var symbolPattern = new Regex(@"^[a-zA-Z0-9]$"); var illegalSymbols = alphabet.FindAll(s => !symbolPattern.IsMatch(s)); if (illegalSymbols.Count > 0) { var message = string.Format( "Found illegal symbols {0} in alphabet. Symbols should match [a-zA-Z0-9]", string.Join(", ", illegalSymbols) ); throw new PumpingLemmaException(message); } // Parse the language var ss = PumpingLemma.Parser.parseSymbolicString(symbolicStringText, alphabet); if (ss == null) { throw new PumpingLemmaException("Unable to parse language"); } // Parse the constraintDesc var constraint = PumpingLemma.Parser.parseCondition(constraintText); if (constraint == null) { throw new PumpingLemmaException("Unable to parse constraint"); } // Make sure all the variables are bound var boundVariables = ss.GetIntegerVariables(); var constraintVariables = constraint.GetVariables(); // Console.WriteLine("Bound variables: " + String.Join(", ", boundVariables)); // Console.WriteLine("Constriant variables: " + String.Join(", ", constraintVariables)); foreach (var consVar in constraintVariables) { if (!boundVariables.Contains(consVar)) { throw new PumpingLemmaException( string.Format("Constraint variable {0} not bound", consVar)); } } // Add constraints saying that all variables are >= 0 BooleanExpression defaultConstraint = LogicalExpression.True(); foreach (var consVar in constraintVariables) { defaultConstraint = LogicalExpression.And( defaultConstraint, ComparisonExpression.GreaterThanOrEqual( LinearIntegerExpression.SingleTerm(1, consVar), LinearIntegerExpression.Constant(0) ) ); } return(new ArithmeticLanguage(alphabet, ss, constraint)); }
public static BooleanExpression And(params BooleanExpression[] ops) { return(ops.Aggregate(LogicalExpression.True() as BooleanExpression, (x, y) => And(x, y))); }