public void MessageOpsTest() { const double Eps = 1e-6; StringDistribution str1 = StringOfLengthOp.StrAverageConditional(DiscreteChar.Letter(), 10); Assert.Equal(StringDistribution.Repeat(ImmutableDiscreteChar.Letter(), 10, 10), str1); StringDistribution str2 = StringOfLengthOp.StrAverageConditional( DiscreteChar.PointMass('a'), Discrete.UniformInRange(5, 2, 4)); Assert.Equal(StringDistribution.OneOf("aa", "aaa", "aaaa"), str2); StringDistribution str3 = StringOfLengthOp.StrAverageConditional( DiscreteChar.OneOf('a', 'b'), new Discrete(0.1, 0.0, 0.6, 0.3)); StringInferenceTestUtilities.TestProbability(str3, 0.1, string.Empty); StringInferenceTestUtilities.TestProbability(str3, 0.6 / 4, "aa", "ab", "ba", "bb"); StringInferenceTestUtilities.TestProbability(str3, 0.3 / 8, "aaa", "bbb", "abb", "bab"); Discrete length1 = StringOfLengthOp.LengthAverageConditional( StringDistribution.OneOf("aa", "bbb"), DiscreteChar.PointMass('a'), Discrete.Uniform(10)); Assert.Equal(Discrete.PointMass(2, 10), length1); Discrete length2 = StringOfLengthOp.LengthAverageConditional( StringDistribution.OneOf("aab", "ab", "b", "bc"), DiscreteChar.OneOf('a', 'b'), Discrete.Uniform(10)); Assert.Equal(4.0 / 7.0, length2[1], Eps); Assert.Equal(2.0 / 7.0, length2[2], Eps); Assert.Equal(1.0 / 7.0, length2[3], Eps); }
public static StringDistribution SubAverageConditional(StringDistribution str, int start, int minLength, int maxLength) { Argument.CheckIfNotNull(str, "str"); Argument.CheckIfInRange(start >= 0, "start", "Start index must be non-negative."); Argument.CheckIfInRange(minLength >= 0, "minLength", "Min length must be non-negative."); Argument.CheckIfInRange(maxLength >= 0, "maxLength", "Max length must be non-negative."); if (str.IsPointMass) { var strPoint = str.Point; var alts = new HashSet <string>(); for (int length = minLength; length <= maxLength; length++) { var s = strPoint.Substring(start, Math.Min(length, strPoint.Length)); alts.Add(s); } return(StringDistribution.OneOf(alts)); } var anyChar = StringAutomaton.ConstantOnElement(1.0, ImmutableDiscreteChar.Any()); var transducer = StringTransducer.Consume(StringAutomaton.Repeat(anyChar, minTimes: start, maxTimes: start)); transducer.AppendInPlace(StringTransducer.Copy(StringAutomaton.Repeat(anyChar, minTimes: minLength, maxTimes: maxLength))); transducer.AppendInPlace(StringTransducer.Consume(StringAutomaton.Constant(1.0))); return(StringDistribution.FromWeightFunction(transducer.ProjectSource(str.ToAutomaton()))); }
public void CopyElement() { StringTransducer copy = StringTransducer.CopyElement(ImmutableDiscreteChar.OneOf('a', 'b')); StringInferenceTestUtilities.TestTransducerValue(copy, "a", "a", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "b", "b", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "a", "b", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "b", "a", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, string.Empty, string.Empty, 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "bb", "bb", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "bab", "bab", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "bab", "ba", 0.0); //// Tests that projection on CopyElement(elements) shrinks the support StringAutomaton automaton = StringAutomaton.ConstantOn(2.0, "a", "ab", "ac"); automaton = automaton.Sum(StringAutomaton.ConstantOn(1.0, "a")); automaton = automaton.Sum(StringAutomaton.Constant(2.0)); automaton = automaton.Product(StringAutomaton.Constant(3.0)); for (int i = 0; i < 2; ++i) { StringInferenceTestUtilities.TestValue(automaton, 15, "a"); StringInferenceTestUtilities.TestValue(automaton, 6.0, "b"); StringInferenceTestUtilities.TestValue(automaton, i == 0 ? 6.0 : 0.0, string.Empty); StringInferenceTestUtilities.TestValue(automaton, i == 0 ? 12.0 : 0.0, "ac", "ab"); automaton = copy.ProjectSource(automaton); } }
/// <summary> /// Creates a uniform distribution over any string starting and ending with a non-word character, /// with a length in given bounds. /// Characters other than the first and the last are restricted to be non-zero probability characters /// from a given distribution. /// </summary> /// <param name="minLength">The minimum allowed string length.</param> /// <param name="maxLength">The maximum allowed string length.</param> /// <param name="allowedChars">The distribution representing allowed characters.</param> /// <returns>The created distribution.</returns> public static StringDistribution WordMiddle(int minLength, int maxLength, ImmutableDiscreteChar allowedChars) { if (maxLength < minLength) { throw new ArgumentException("The maximum length cannot be less than the minimum length."); } if (minLength < 1) { throw new ArgumentException("The minimum length must be at least one."); } var nonWordChar = StringDistribution.Char(NonWordCharacter); if ((minLength == 1) && (maxLength == 1)) { return(nonWordChar); } // TODO: make a PartialUniform copy of allowedChars var suffix = StringDistribution.Repeat(allowedChars, minTimes: Math.Max(minLength - 2, 0), maxTimes: maxLength - 2); suffix.AppendInPlace(nonWordChar); if (minLength == 1) { var allowedChar = allowedChars.GetMode(); var allowedSuffix = new string(Enumerable.Repeat(allowedChar, Math.Max(minLength - 2, 0)).ToArray()) + ' '; var suffixLogProb = suffix.GetLogProb(allowedSuffix); suffix.SetToSumLog(suffixLogProb, StringDistribution.Empty(), 0.0, suffix); } return(nonWordChar + suffix); }
public void GetOutgoingTransitionsForDeterminization1() { var builder = new StringAutomaton.Builder(); builder.Start.AddTransition(ImmutableDiscreteChar.Uniform(), Weight.FromValue(2)); var wrapper = new StringAutomatonWrapper(builder); var outgoingTransitions = wrapper.GetOutgoingTransitionsForDeterminization(0, Weight.FromValue(3)); var expectedOutgoingTransitions = new[]
public void ReplaceElements() { StringTransducer replace = StringTransducer.Replace(ImmutableDiscreteChar.Lower(), ImmutableDiscreteChar.Digit()); StringInferenceTestUtilities.TestTransducerValue(replace, "hello", "123", 1.0); StringInferenceTestUtilities.TestTransducerValue(replace, "w", "1337", 1.0); StringInferenceTestUtilities.TestTransducerValue(replace, "w", string.Empty, 1.0); StringInferenceTestUtilities.TestTransducerValue(replace, string.Empty, "17", 1.0); StringInferenceTestUtilities.TestTransducerValue(replace, string.Empty, string.Empty, 1.0); StringInferenceTestUtilities.TestTransducerValue(replace, "123", "worlds", 0.0); StringInferenceTestUtilities.TestTransducerValue(replace, "123", "123", 0.0); StringInferenceTestUtilities.TestTransducerValue(replace, "1", string.Empty, 0.0); }
public static StringDistribution EmptyOrStartsWith(ImmutableDiscreteChar charsInMainString, ImmutableDiscreteChar startsWith) { // TODO: fix equality and then use factory methods to create this var result = new StringAutomaton.Builder(); result.Start.SetEndWeight(Weight.One); var otherState = result.Start.AddTransition(startsWith, Weight.FromLogValue(-startsWith.GetLogAverageOf(startsWith))); otherState.AddSelfTransition(charsInMainString, Weight.FromLogValue(-charsInMainString.GetLogAverageOf(charsInMainString))); otherState.SetEndWeight(Weight.One); return(StringDistribution.FromWeightFunction(result.GetAutomaton())); }
/// <summary> /// Initializes static members of the <see cref="StringFormatOpBase{TThis}"/> class. /// </summary> static StringFormatOpBase() { // More general behavior by default RequirePlaceholderForEveryArgument = false; var noBraces = ImmutableDiscreteChar.OneOf('{', '}').Complement(); DisallowBracesAutomaton = StringAutomaton.Constant(1.0, noBraces); DisallowBracesTransducer = StringTransducer.Copy(noBraces); // Make sure that the static constructor of TThis has been invoked so that TThis sets everything up new TThis(); }
/// <include file='FactorDocs.xml' path='factor_docs/message_op_class[@name="SubstringOp"]/message_doc[@name="StrAverageConditional(StringDistribution, int, int)"]/*'/> public static StringDistribution StrAverageConditional(StringDistribution sub, int start, int length) { Argument.CheckIfNotNull(sub, "sub"); Argument.CheckIfInRange(start >= 0, "start", "Start index must be non-negative."); Argument.CheckIfInRange(length >= 0, "length", "Length must be non-negative."); var anyChar = StringAutomaton.ConstantOnElement(1.0, ImmutableDiscreteChar.Any()); var transducer = StringTransducer.Produce(StringAutomaton.Repeat(anyChar, minTimes: start, maxTimes: start)); transducer.AppendInPlace(StringTransducer.Copy(StringAutomaton.Repeat(anyChar, minTimes: length, maxTimes: length))); transducer.AppendInPlace(StringTransducer.Produce(StringAutomaton.Constant(1.0))); return(StringDistribution.FromWeightFunction(transducer.ProjectSource(sub.ToAutomaton()))); }
public void Repeat2() { StringAutomaton automaton = StringAutomaton.Constant(1.0, ImmutableDiscreteChar.Lower()); StringInferenceTestUtilities.TestValue(automaton, 1.0, string.Empty, "ab", "abcab"); automaton = StringAutomaton.Repeat(automaton); // Can't use StringInferenceTestUtilities.TestValue here since the value is not infinite in log-domain // due to approximate closure computations for epsilon-loops Assert.True(automaton.GetValue(string.Empty) > 1000); Assert.True(automaton.GetValue("ab") > 1000); Assert.True(automaton.GetValue("abcab") > 1000); }
public void Optional() { StringAutomaton automaton = StringAutomaton.Constant(1.0, ImmutableDiscreteChar.Lower()); StringTransducer copy = StringTransducer.Copy(automaton); StringTransducer copyOptional = StringTransducer.Optional(copy); StringInferenceTestUtilities.TestTransducerValue(copy, "abc", "abc", 1.0); StringInferenceTestUtilities.TestTransducerValue(copyOptional, "abc", "abc", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, string.Empty, string.Empty, 1.0); StringInferenceTestUtilities.TestTransducerValue(copyOptional, string.Empty, string.Empty, 2.0); StringInferenceTestUtilities.TestTransducerValue(copy, "abc", "ab", 0.0); StringInferenceTestUtilities.TestTransducerValue(copyOptional, "abc", "ab", 0.0); StringInferenceTestUtilities.TestTransducerValue(copy, "abc", string.Empty, 0.0); StringInferenceTestUtilities.TestTransducerValue(copyOptional, "abc", string.Empty, 0.0); }
public void Repeat1() { StringAutomaton automaton = StringAutomaton.ConstantOn(1.0, "abc") .Append(StringAutomaton.Constant(2.0, ImmutableDiscreteChar.Upper())); StringInferenceTestUtilities.TestValue(automaton, 0.0, string.Empty, "ab", "abcab", "XYZ"); StringInferenceTestUtilities.TestValue(automaton, 2.0, "abc", "abcX", "abcXXYZ"); StringAutomaton loopyAutomaton = StringAutomaton.Repeat(automaton); StringInferenceTestUtilities.TestValue(loopyAutomaton, 0.0, string.Empty, "ab", "abcab", "XYZ"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 2.0, "abc", "abcA"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 4.0, "abcabc", "abcabcX", "abcABCabc", "abcXabcYZ"); StringInferenceTestUtilities.TestValue(loopyAutomaton, 8.0, "abcabcabc", "abcXabcYabcZZ"); }
public void ConsumeAutomaton() { StringAutomaton automaton = StringAutomaton.Constant(2.0, ImmutableDiscreteChar.Lower()); automaton = automaton.Sum(StringAutomaton.ConstantOnElement(3.0, 'a')); StringTransducer consume = StringTransducer.Consume(automaton); StringInferenceTestUtilities.TestTransducerValue(consume, "aaa", string.Empty, 2.0); StringInferenceTestUtilities.TestTransducerValue(consume, "bb", string.Empty, 2.0); StringInferenceTestUtilities.TestTransducerValue(consume, "a", string.Empty, 5.0); StringInferenceTestUtilities.TestTransducerValue(consume, string.Empty, string.Empty, 2.0); StringInferenceTestUtilities.TestTransducerValue(consume, "bb", "aaa", 0.0); StringInferenceTestUtilities.TestTransducerValue(consume, "bb", "bb", 0.0); StringInferenceTestUtilities.TestTransducerValue(consume, string.Empty, "bb", 0.0); StringInferenceTestUtilities.TestTransducerValue(consume, string.Empty, "a", 0.0); }
public void Sum() { StringTransducer replace = StringTransducer.Sum( StringTransducer.Replace(ImmutableDiscreteChar.Lower(), ImmutableDiscreteChar.Digit()), StringTransducer.Replace(ImmutableDiscreteChar.Lower(), ImmutableDiscreteChar.LetterOrDigit())); StringInferenceTestUtilities.TestTransducerValue(replace, "hello", "123", 2.0); StringInferenceTestUtilities.TestTransducerValue(replace, "w", "1337", 2.0); StringInferenceTestUtilities.TestTransducerValue(replace, "w", string.Empty, 2.0); StringInferenceTestUtilities.TestTransducerValue(replace, string.Empty, "17", 2.0); StringInferenceTestUtilities.TestTransducerValue(replace, string.Empty, string.Empty, 2.0); StringInferenceTestUtilities.TestTransducerValue(replace, "hello", "worlds", 1.0); StringInferenceTestUtilities.TestTransducerValue(replace, "hello", "WORLDS111", 1.0); StringInferenceTestUtilities.TestTransducerValue(replace, "123", "worlds", 0.0); StringInferenceTestUtilities.TestTransducerValue(replace, "123", "123", 0.0); StringInferenceTestUtilities.TestTransducerValue(replace, "1", string.Empty, 0.0); }
public void CopyAutomaton() { StringAutomaton automaton = StringAutomaton.ConstantOn(1.0, "prefix1", "prefix2") .Append(StringAutomaton.Constant(1.0, ImmutableDiscreteChar.Lower())) .Append(StringAutomaton.Constant(1.0, ImmutableDiscreteChar.Upper())) .Append("!"); StringTransducer copy = StringTransducer.Copy(automaton); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix1!", "prefix1!", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix2!", "prefix2!", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix1lower!", "prefix1lower!", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix2UPPER!", "prefix2UPPER!", 1.0); StringInferenceTestUtilities.TestTransducerValue(copy, "prefix1lowerUPPER!", "prefix1lowerUPPER!", 1.0); StringInferenceTestUtilities.TestIfTransducerRejects(copy, "prefix1lower", "prefix2UPPER", "!", "prefix1lowerUPPER"); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix1!", 1.0); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix2!", 1.0); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix1lower!", 1.0); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix2UPPER!", 1.0); StringInferenceTestUtilities.TestTransducerProjection(copy, automaton, "prefix1lowerUPPER!", 1.0); StringAutomaton subsetAutomaton = StringAutomaton.ConstantOn(2.0, "prefix1") .Append(StringAutomaton.ConstantOn(3.0, "lll", "mmm")) .Append(StringAutomaton.ConstantOn(1.5, "!", "U!")); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix1lll!", 9.0); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix1mmmU!", 9.0); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix1!", 0.0); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix2lower!", 0.0); StringInferenceTestUtilities.TestTransducerProjection(copy, subsetAutomaton, "prefix2U!", 0.0); StringAutomaton supersetAutomaton = StringAutomaton.ConstantOn(1.5, "pr") .Append(StringAutomaton.Constant(2.0)); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix1!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix2!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix1lower!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix2UPPER!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix1lowerUPPER!", 3.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix11!", 0.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prefix1lowerUPPERlower!", 0.0); StringInferenceTestUtilities.TestTransducerProjection(copy, supersetAutomaton, "prrrrr!", 0.0); }
public void ReplaceAutomaton() { StringAutomaton automaton1 = StringAutomaton.Constant(2.0, ImmutableDiscreteChar.Lower()); automaton1 = automaton1.Sum(StringAutomaton.ConstantOnElement(3.0, 'a')); StringAutomaton automaton2 = StringAutomaton.Constant(0.5, ImmutableDiscreteChar.Digit()); automaton2 = automaton2.Sum(StringAutomaton.Constant(2.5, ImmutableDiscreteChar.LetterOrDigit())); StringTransducer replace = StringTransducer.Replace(automaton1, automaton2); StringInferenceTestUtilities.TestTransducerValue(replace, string.Empty, "123", 6.0); StringInferenceTestUtilities.TestTransducerValue(replace, "a", "123", 15.0); StringInferenceTestUtilities.TestTransducerValue(replace, "ax", "AbC", 5.0); StringInferenceTestUtilities.TestTransducerValue(replace, "a", "a", 12.5); StringInferenceTestUtilities.TestTransducerValue(replace, string.Empty, string.Empty, 6.0); StringInferenceTestUtilities.TestTransducerValue(replace, "123", string.Empty, 0.0); StringInferenceTestUtilities.TestTransducerValue(replace, "AbC", "ax", 0.0); StringInferenceTestUtilities.TestTransducerValue(replace, "1", "1", 0.0); }
/// <summary> /// Creates a uniform distribution over any string starting and ending with a non-word character. /// Characters other than the first and the last are restricted to be non-zero probability characters /// from a given distribution. /// </summary> /// <param name="allowedChars">The distribution representing allowed characters.</param> /// <param name="nonWordCharacter">The word separating characters.</param> /// <returns>The created distribution.</returns> public static StringDistribution WordMiddle(ImmutableDiscreteChar allowedChars, ImmutableDiscreteChar?nonWordCharacter = null) { // TODO: fix equality and then use factory methods to create this nonWordCharacter = nonWordCharacter ?? NonWordCharacter; var result = new StringAutomaton.Builder(); var otherState1 = result.Start.AddTransition( Option.FromNullable(nonWordCharacter), Weight.FromLogValue(-nonWordCharacter.Value.GetLogAverageOf(nonWordCharacter.Value))); otherState1.SetEndWeight(Weight.One); var otherState2 = otherState1.AddEpsilonTransition(Weight.One) .AddSelfTransition(allowedChars, Weight.FromLogValue(-allowedChars.GetLogAverageOf(allowedChars))).AddTransition( Option.FromNullable(nonWordCharacter), Weight.FromLogValue(-nonWordCharacter.Value.GetLogAverageOf(nonWordCharacter.Value))); otherState2.SetEndWeight(Weight.One); return(StringDistribution.FromWeightFunction(result.GetAutomaton())); }
private static StringDistribution WordString() => StringDistribution.OneOrMore(ImmutableDiscreteChar.InRanges("azAZ09 __''\t\r"));
[Trait("Category", "OpenBug")] // Test failing with AutomatonTooLarge due to determinization added to SetToProduct in change 47614. Increasing max states to 1M does not fix the issue public void PropertyInferencePerformanceTest() { Rand.Restart(777); var namesData = new[] { "Alice", "Bob", "Charlie", "Eve", "Boris", "John" }; var valueData = new[] { "sender", "receiver", "attacker", "eavesdropper", "developer", "researcher" }; var templatesData = new[] { "{0} is {1}", "{0} is known as {1}", "{1} is a role of {0}", "{0} -- {1}", "{0} aka {1}" }; var textsData = new string[10]; for (int i = 0; i < textsData.Length; ++i) { int entityIndex = Rand.Int(namesData.Length); int templateIndex = Rand.Int(templatesData.Length); textsData[i] = string.Format(templatesData[templateIndex], namesData[entityIndex], valueData[entityIndex]); } var entity = new Range(namesData.Length).Named("entity"); var template = new Range(templatesData.Length).Named("template"); var text = new Range(textsData.Length).Named("text"); var entityNames = Variable.Array <string>(entity).Named("entityNames"); entityNames[entity] = Variable.Random(StringDistribution.Capitalized()).ForEach(entity); var entityValues = Variable.Array <string>(entity).Named("entityValues"); entityValues[entity] = Variable.Random(StringDistribution.Lower()).ForEach(entity); StringDistribution templatePriorMiddle = StringDistribution.ZeroOrMore(ImmutableDiscreteChar.OneOf('{', '}').Complement()); StringDistribution templatePrior = StringDistribution.OneOf( StringDistribution.String("{0} ") + templatePriorMiddle + StringDistribution.String(" {1}"), StringDistribution.String("{1} ") + templatePriorMiddle + StringDistribution.String(" {0}")); var templates = Variable.Array <string>(template).Named("templates"); templates[template] = Variable.Random(templatePrior).ForEach(template); var texts = Variable.Array <string>(text).Named("texts"); using (Variable.ForEach(text)) { var entityIndex = Variable.DiscreteUniform(entity).Named("entityIndex"); var templateIndex = Variable.DiscreteUniform(template).Named("templateIndex"); using (Variable.Switch(entityIndex)) using (Variable.Switch(templateIndex)) { texts[text] = Variable.StringFormat(templates[templateIndex], entityNames[entityIndex], entityValues[entityIndex]); } } texts.ObservedValue = textsData; var engine = new InferenceEngine(); engine.ShowProgress = false; engine.OptimiseForVariables = new[] { entityNames, entityValues }; engine.Compiler.RecommendedQuality = QualityBand.Experimental; // TODO: get this test to work with parallel for loops. engine.Compiler.UseParallelForLoops = false; engine.NumberOfIterations = 1; ProfileAction( () => { Console.WriteLine(engine.Infer <StringDistribution[]>(entityNames)[0]); Console.WriteLine(engine.Infer <StringDistribution[]>(entityValues)[0]); }, 1); }
/// <summary> /// Creates a uniform distribution over any string starting and ending with a non-word character, /// with a length in given bounds. /// </summary> /// <param name="minLength">The minimum allowed string length.</param> /// <param name="maxLength">The maximum allowed string length.</param> /// <returns>The created distribution.</returns> public static StringDistribution WordMiddle(int minLength, int maxLength) => WordMiddle(minLength, maxLength, ImmutableDiscreteChar.Any());
/// <summary> /// Creates a uniform distribution over either the empty string or any string starting with a non-word character. /// </summary> /// <returns>The created distribution.</returns> public static StringDistribution WordSuffix() => WordSuffix(ImmutableDiscreteChar.Any());
/// <summary> /// Creates a uniform distribution over either the empty string or any string starting with a non-word character. /// Characters other than the last are restricted to be non-zero probability characters from a given distribution. /// </summary> /// <param name="allowedChars">The distribution representing allowed characters.</param> /// <returns>The created distribution.</returns> public static StringDistribution WordSuffix(ImmutableDiscreteChar allowedChars) => EmptyOrStartsWith(allowedChars, NonWordCharacter);
/// <summary> /// Creates a uniform distribution over any string starting and ending with a non-word character (possibly of length 1). /// </summary> /// <returns>The created distribution.</returns> public static StringDistribution WordMiddle() => WordMiddle(ImmutableDiscreteChar.Any());