source: branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs @ 16026

Last change on this file since 16026 was 16026, checked in by bburlacu, 21 months ago

#2886:

  • replace functionally-overlapping classes Production and SymbolString with a single class SymbolList
  • refactor methods from Grammar class as methods and properties of SymbolList
  • add parameter for the number of constant optimization iterations
  • refactor code
File size: 23.6 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Threading;
5using HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration.GrammarEnumeration;
6using HeuristicLab.Collections;
7using HeuristicLab.Common;
8using HeuristicLab.Core;
9using HeuristicLab.Data;
10using HeuristicLab.Optimization;
11using HeuristicLab.Parameters;
12using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
13using HeuristicLab.Problems.DataAnalysis;
14using HeuristicLab.Problems.DataAnalysis.Symbolic;
15using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
16
17namespace HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration {
18  [Item("Grammar Enumeration Symbolic Regression", "Iterates all possible model structures for a fixed grammar.")]
19  [StorableClass]
20  [Creatable(CreatableAttribute.Categories.DataAnalysisRegression, Priority = 250)]
21  public class GrammarEnumerationAlgorithm : FixedDataAnalysisAlgorithm<IRegressionProblem> {
22    #region properties and result names
23    private readonly string SearchStructureSizeName = "Search Structure Size";
24    private readonly string GeneratedPhrasesName = "Generated/Archived Phrases";
25    private readonly string GeneratedSentencesName = "Generated Sentences";
26    private readonly string DistinctSentencesName = "Distinct Sentences";
27    private readonly string PhraseExpansionsName = "Phrase Expansions";
28    private readonly string AverageSentenceComplexityName = "Avg. Sentence Complexity among Distinct";
29    private readonly string OverwrittenSentencesName = "Sentences overwritten";
30    private readonly string AnalyzersParameterName = "Analyzers";
31    private readonly string ExpansionsPerSecondName = "Expansions per second";
32
33    private readonly string OptimizeConstantsParameterName = "Optimize Constants";
34    private readonly string ConstantOptimizationIterationsParameterName = "Constant Optimization Iterations";
35    private readonly string ErrorWeightParameterName = "Error Weight";
36    private readonly string SearchDataStructureParameterName = "Search Data Structure";
37    private readonly string MaxComplexityParameterName = "Max. Complexity";
38    private readonly string GuiUpdateIntervalParameterName = "GUI Update Interval";
39    private readonly string GrammarSymbolsParameterName = "Grammar Symbols";
40    private readonly string SearchDataStructureSizeParameterName = "Search Data Structure Size";
41
42    // result names
43    public static readonly string BestTrainingModelResultName = "Best model (Training)";
44    public static readonly string BestTrainingSolutionResultName = "Best solution (Training)";
45    public static readonly string BestComplexityResultName = "Best solution complexity";
46
47    public override bool SupportsPause { get { return true; } }
48
49    protected IFixedValueParameter<BoolValue> OptimizeConstantsParameter {
50      get { return (IFixedValueParameter<BoolValue>)Parameters[OptimizeConstantsParameterName]; }
51    }
52
53    public bool OptimizeConstants {
54      get { return OptimizeConstantsParameter.Value.Value; }
55      set { OptimizeConstantsParameter.Value.Value = value; }
56    }
57
58    public int ConstantOptimizationIterations {
59      get { return ConstantOptimizationIterationsParameter.Value.Value; }
60      set { ConstantOptimizationIterationsParameter.Value.Value = value; }
61    }
62
63    public IFixedValueParameter<IntValue> ConstantOptimizationIterationsParameter {
64      get { return (IFixedValueParameter<IntValue>)Parameters[ConstantOptimizationIterationsParameterName]; }
65    }
66
67    protected IFixedValueParameter<IntValue> MaxComplexityParameter {
68      get { return (IFixedValueParameter<IntValue>)Parameters[MaxComplexityParameterName]; }
69    }
70
71    public int MaxComplexity {
72      get { return MaxComplexityParameter.Value.Value; }
73      set { MaxComplexityParameter.Value.Value = value; }
74    }
75
76    protected IFixedValueParameter<DoubleValue> ErrorWeightParameter {
77      get { return (IFixedValueParameter<DoubleValue>)Parameters[ErrorWeightParameterName]; }
78    }
79
80    public double ErrorWeight {
81      get { return ErrorWeightParameter.Value.Value; }
82      set { ErrorWeightParameter.Value.Value = value; }
83    }
84
85    protected IFixedValueParameter<IntValue> GuiUpdateIntervalParameter {
86      get { return (IFixedValueParameter<IntValue>)Parameters[GuiUpdateIntervalParameterName]; }
87    }
88
89    public int GuiUpdateInterval {
90      get { return GuiUpdateIntervalParameter.Value.Value; }
91      set { GuiUpdateIntervalParameter.Value.Value = value; }
92    }
93
94    protected IFixedValueParameter<EnumValue<StorageType>> SearchDataStructureParameter {
95      get { return (IFixedValueParameter<EnumValue<StorageType>>)Parameters[SearchDataStructureParameterName]; }
96    }
97
98    public IFixedValueParameter<IntValue> SearchDataStructureSizeParameter {
99      get { return (IFixedValueParameter<IntValue>)Parameters[SearchDataStructureSizeParameterName]; }
100    }
101
102    public int SearchDataStructureSize {
103      get { return SearchDataStructureSizeParameter.Value.Value; }
104    }
105
106    public StorageType SearchDataStructure {
107      get { return SearchDataStructureParameter.Value.Value; }
108      set { SearchDataStructureParameter.Value.Value = value; }
109    }
110
111    public IFixedValueParameter<ReadOnlyCheckedItemCollection<IGrammarEnumerationAnalyzer>> AnalyzersParameter {
112      get { return (IFixedValueParameter<ReadOnlyCheckedItemCollection<IGrammarEnumerationAnalyzer>>)Parameters[AnalyzersParameterName]; }
113    }
114
115    public ICheckedItemCollection<IGrammarEnumerationAnalyzer> Analyzers {
116      get { return AnalyzersParameter.Value; }
117    }
118
119    public IFixedValueParameter<ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>>> GrammarSymbolsParameter {
120      get { return (IFixedValueParameter<ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>>>)Parameters[GrammarSymbolsParameterName]; }
121    }
122
123    public ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>> GrammarSymbols {
124      get { return GrammarSymbolsParameter.Value; }
125    }
126
127    [Storable]
128    public SymbolList BestTrainingSentence { get; set; }     // Currently set in RSquaredEvaluator: quite hacky, but makes testing much easier for now...
129    #endregion
130
131    [Storable]
132    public Dictionary<int, int> DistinctSentencesComplexity { get; private set; }  // Semantically distinct sentences and their length in a run.
133
134    [Storable]
135    public HashSet<int> ArchivedPhrases { get; private set; }
136
137    [Storable]
138    internal SearchDataStore OpenPhrases { get; private set; }           // Stack/Queue/etc. for fetching the next node in the search tree. 
139
140    [Storable]
141    public int MaxSentenceLength { get; private set; }
142
143    #region execution stats
144    [Storable]
145    public int AllGeneratedSentencesCount { get; private set; }
146
147    [Storable]
148    public int OverwrittenSentencesCount { get; private set; } // It is not guaranteed that shorter solutions are found first.
149                                                               // When longer solutions are overwritten with shorter ones,
150                                                               // this counter is increased.
151    [Storable]
152    public int PhraseExpansionCount { get; private set; }      // Number, how many times a nonterminal symbol is replaced with a production rule.
153    #endregion
154
155    [Storable]
156    public Grammar Grammar { get; private set; }
157
158    #region ctors
159    public override IDeepCloneable Clone(Cloner cloner) {
160      return new GrammarEnumerationAlgorithm(this, cloner);
161    }
162
163    [StorableConstructor]
164    protected GrammarEnumerationAlgorithm(bool deserializing) : base(deserializing) { }
165
166    private void RegisterEvents() {
167      // re-wire analyzer events
168      foreach (var analyzer in Analyzers.CheckedItems)
169        analyzer.Register(this);
170      Analyzers.CheckedItemsChanged += Analyzers_CheckedItemsChanged;
171
172      SearchDataStructureParameter.Value.ValueChanged += (o, e) => Prepare();
173      SearchDataStructureSizeParameter.Value.ValueChanged += (o, e) => Prepare();
174    }
175
176    private void DeregisterEvents() {
177      foreach (var analyzer in Analyzers.CheckedItems)
178        analyzer.Register(this);
179      Analyzers.CheckedItemsChanged -= Analyzers_CheckedItemsChanged;
180
181      SearchDataStructureParameter.Value.ValueChanged -= (o, e) => Prepare();
182      SearchDataStructureSizeParameter.Value.ValueChanged -= (o, e) => Prepare();
183    }
184
185    [StorableHook(HookType.AfterDeserialization)]
186    private void AfterDeserialization() {
187      RegisterEvents();
188    }
189
190    public GrammarEnumerationAlgorithm() {
191      Parameters.Add(new FixedValueParameter<BoolValue>(OptimizeConstantsParameterName, "Run constant optimization in sentence evaluation.", new BoolValue(false)));
192      Parameters.Add(new FixedValueParameter<DoubleValue>(ErrorWeightParameterName, "Defines, how much weight is put on a phrase's r² value when priorizing phrases during search.", new DoubleValue(0.8)));
193      Parameters.Add(new FixedValueParameter<IntValue>(MaxComplexityParameterName, "The maximum number of variable symbols in a sentence.", new IntValue(12)));
194      Parameters.Add(new FixedValueParameter<IntValue>(GuiUpdateIntervalParameterName, "Number of generated sentences, until GUI is refreshed.", new IntValue(5000)));
195      Parameters.Add(new FixedValueParameter<IntValue>(SearchDataStructureSizeParameterName, "The size of the search data structure.", new IntValue((int)1e5)));
196      Parameters.Add(new FixedValueParameter<EnumValue<StorageType>>(SearchDataStructureParameterName, new EnumValue<StorageType>(StorageType.SortedSet)));
197      Parameters.Add(new FixedValueParameter<IntValue>(ConstantOptimizationIterationsParameterName, new IntValue(10)));
198
199      SearchDataStructureParameter.Value.ValueChanged += (o, e) => Prepare();
200      SearchDataStructureSizeParameter.Value.ValueChanged += (o, e) => Prepare();
201
202      var availableAnalyzers = new IGrammarEnumerationAnalyzer[] {
203        new SearchGraphVisualizer(),
204        new SentenceLogger(),
205        new RSquaredEvaluator()
206      };
207
208      Parameters.Add(new FixedValueParameter<ReadOnlyCheckedItemCollection<IGrammarEnumerationAnalyzer>>(
209        AnalyzersParameterName,
210        new CheckedItemCollection<IGrammarEnumerationAnalyzer>(availableAnalyzers).AsReadOnly()));
211
212      Analyzers.CheckedItemsChanged += Analyzers_CheckedItemsChanged;
213
214      foreach (var analyzer in Analyzers) {
215        Analyzers.SetItemCheckedState(analyzer, false);
216      }
217      Analyzers.SetItemCheckedState(Analyzers.First(analyzer => analyzer is RSquaredEvaluator), true);
218
219      var grammarSymbols = Enum.GetValues(typeof(GrammarRule))
220        .Cast<GrammarRule>()
221        .Select(v => new EnumValue<GrammarRule>(v));
222
223      Parameters.Add(new FixedValueParameter<ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>>>(
224        GrammarSymbolsParameterName,
225        new ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>>(new CheckedItemCollection<EnumValue<GrammarRule>>(grammarSymbols))
226      ));
227      foreach (EnumValue<GrammarRule> grammarSymbol in GrammarSymbols) {
228        GrammarSymbols.SetItemCheckedState(grammarSymbol, true);
229      }
230
231      // set a default problem
232      Problem = new RegressionProblem() {
233        ProblemData = new Problems.Instances.DataAnalysis.PolyTen(seed: 1234).GenerateRegressionData()
234      };
235    }
236
237    public GrammarEnumerationAlgorithm(GrammarEnumerationAlgorithm original, Cloner cloner) : base(original, cloner) {
238      foreach (var analyzer in Analyzers.CheckedItems)
239        analyzer.Register(this);
240      Analyzers.CheckedItemsChanged += Analyzers_CheckedItemsChanged;
241
242      DistinctSentencesComplexity = new Dictionary<int, int>(original.DistinctSentencesComplexity);
243      ArchivedPhrases = new HashSet<int>(original.ArchivedPhrases);
244      OpenPhrases = cloner.Clone(original.OpenPhrases);
245      Grammar = cloner.Clone(original.Grammar);
246
247      AllGeneratedSentencesCount = original.AllGeneratedSentencesCount;
248      OverwrittenSentencesCount = original.OverwrittenSentencesCount;
249      PhraseExpansionCount = original.PhraseExpansionCount;
250    }
251    #endregion
252
253    public override void Prepare() {
254      DistinctSentencesComplexity = new Dictionary<int, int>();
255      ArchivedPhrases = new HashSet<int>();
256      AllGeneratedSentencesCount = 0;
257      OverwrittenSentencesCount = 0;
258      PhraseExpansionCount = 0;
259
260      Grammar = new Grammar(Problem.ProblemData.AllowedInputVariables.ToArray(), GrammarSymbols.CheckedItems.Select(v => v.Value));
261      OpenPhrases = new SearchDataStore(SearchDataStructure, SearchDataStructureSize); // Select search strategy
262      base.Prepare(); // this actually clears the results which will get reinitialized on Run()
263    }
264
265    protected override void Run(CancellationToken cancellationToken) {
266      // do not reinitialize the algorithm if we're resuming from pause
267      if (previousExecutionState != ExecutionState.Paused) {
268        InitResults();
269        var phrase0 = new SymbolList(new[] { Grammar.StartSymbol });
270        var phrase0Hash = Grammar.Hasher.CalcHashCode(phrase0);
271
272        OpenPhrases.Store(new SearchNode(phrase0Hash, 0.0, 0.0, phrase0));
273      }
274
275      MaxSentenceLength = Grammar.GetMaxSentenceLength(MaxComplexity);
276      var errorWeight = ErrorWeight;
277      var optimizeConstants = OptimizeConstants; // cache value to avoid parameter lookup
278      var iterations = ConstantOptimizationIterations;
279      // main search loop
280      while (OpenPhrases.Count > 0) {
281        if (cancellationToken.IsCancellationRequested)
282          break;
283
284        SearchNode fetchedSearchNode = OpenPhrases.GetNext();
285
286        if (fetchedSearchNode == null)
287          continue;
288
289        SymbolList currPhrase = fetchedSearchNode.SymbolList;
290
291        OnPhraseFetched(fetchedSearchNode.Hash, currPhrase);
292
293        ArchivedPhrases.Add(fetchedSearchNode.Hash);
294
295        // expand next nonterminal symbols
296        int nonterminalSymbolIndex = currPhrase.NextNonterminalIndex();
297        NonterminalSymbol expandedSymbol = (NonterminalSymbol)currPhrase[nonterminalSymbolIndex];
298        var appliedProductions = Grammar.Productions[expandedSymbol];
299
300        for (int i = 0; i < appliedProductions.Count; i++) {
301          PhraseExpansionCount++;
302
303          SymbolList newPhrase = currPhrase.DerivePhrase(nonterminalSymbolIndex, appliedProductions[i]);
304          int newPhraseComplexity = newPhrase.Complexity;
305
306          if (newPhraseComplexity > MaxComplexity)
307            continue;
308
309          var phraseHash = Grammar.Hasher.CalcHashCode(newPhrase);
310
311          OnPhraseDerived(fetchedSearchNode.Hash, fetchedSearchNode.SymbolList, phraseHash, newPhrase, expandedSymbol, appliedProductions[i]);
312
313          if (newPhrase.IsSentence()) {
314            AllGeneratedSentencesCount++;
315
316            OnSentenceGenerated(fetchedSearchNode.Hash, fetchedSearchNode.SymbolList, phraseHash, newPhrase, expandedSymbol, appliedProductions[i]);
317
318            // Is the best solution found? (only if RSquaredEvaluator is activated)
319            if (Results.ContainsKey(RSquaredEvaluator.BestTrainingQualityResultName)) {
320              double r2 = ((DoubleValue)Results[RSquaredEvaluator.BestTrainingQualityResultName].Value).Value;
321              if (r2.IsAlmost(1.0)) {
322                UpdateView(force: true);
323                return;
324              }
325            }
326
327            if (!DistinctSentencesComplexity.ContainsKey(phraseHash) || DistinctSentencesComplexity[phraseHash] > newPhraseComplexity) {
328              if (DistinctSentencesComplexity.ContainsKey(phraseHash)) OverwrittenSentencesCount++; // for analysis only
329
330              DistinctSentencesComplexity[phraseHash] = newPhraseComplexity;
331              OnDistinctSentenceGenerated(fetchedSearchNode.Hash, fetchedSearchNode.SymbolList, phraseHash, newPhrase, expandedSymbol, appliedProductions[i]);
332            }
333            UpdateView();
334
335          } else if (!OpenPhrases.Contains(phraseHash) && !ArchivedPhrases.Contains(phraseHash)) {
336
337            bool isCompleteSentence = IsCompleteSentence(newPhrase);
338            double r2 = isCompleteSentence ? Grammar.EvaluatePhrase(newPhrase, Problem.ProblemData, optimizeConstants, iterations) : fetchedSearchNode.R2;
339            double phrasePriority = GetPriority(newPhrase, r2);
340
341            SearchNode newSearchNode = new SearchNode(phraseHash, phrasePriority, r2, newPhrase);
342            OpenPhrases.Store(newSearchNode);
343          }
344        }
345      }
346      UpdateView(force: true);
347    }
348
349    protected static double GetPriority(SymbolList phrase, double r2) {
350      return (1 - r2) * phrase.Count;
351    }
352
353    private bool IsCompleteSentence(SymbolList phrase) {
354      return !phrase.Any(x => x is NonterminalSymbol && x != Grammar.Expr);
355    }
356
357    #region pause support
358    private ExecutionState previousExecutionState;
359
360    protected override void OnPaused() {
361      previousExecutionState = this.ExecutionState;
362      base.OnPaused();
363    }
364
365    protected override void OnPrepared() {
366      previousExecutionState = this.ExecutionState;
367      base.OnPrepared();
368    }
369
370    protected override void OnStarted() {
371      previousExecutionState = this.ExecutionState;
372      base.OnStarted();
373    }
374
375    protected override void OnStopped() {
376      previousExecutionState = this.ExecutionState;
377      // free memory at the end of the run (this saves a lot of memory)
378      ArchivedPhrases.Clear();
379      OpenPhrases.Clear();
380      DistinctSentencesComplexity.Clear();
381
382      if (BestTrainingSentence == null) {
383        base.OnStopped();
384        return;
385      }
386
387      var interpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
388      var tree = Grammar.ParseSymbolicExpressionTree(BestTrainingSentence);
389      var model = new SymbolicRegressionModel(Problem.ProblemData.TargetVariable, tree, interpreter);
390
391      SymbolicRegressionConstantOptimizationEvaluator.OptimizeConstants(
392        interpreter,
393        model.SymbolicExpressionTree,
394        Problem.ProblemData,
395        Problem.ProblemData.TrainingIndices,
396        applyLinearScaling: true,
397        maxIterations: ConstantOptimizationIterations,
398        updateVariableWeights: false,
399        updateConstantsInTree: true);
400
401      model.Scale(Problem.ProblemData);
402      var bestTrainingSolution = new SymbolicRegressionSolution(model, Problem.ProblemData);
403
404      Results.AddOrUpdateResult(BestTrainingModelResultName, model);
405      Results.AddOrUpdateResult(BestTrainingSolutionResultName, bestTrainingSolution);
406      Results.AddOrUpdateResult(BestComplexityResultName, new IntValue(BestTrainingSentence.Complexity));
407      base.OnStopped();
408    }
409    #endregion
410
411    #region Visualization in HL
412    // Initialize entries in result set.
413    private void InitResults() {
414      Results.Clear();
415      Results.Add(new Result(GeneratedPhrasesName, new IntValue(0)));
416      Results.Add(new Result(SearchStructureSizeName, new IntValue(0)));
417      Results.Add(new Result(GeneratedSentencesName, new IntValue(0)));
418      Results.Add(new Result(DistinctSentencesName, new IntValue(0)));
419      Results.Add(new Result(PhraseExpansionsName, new IntValue(0)));
420      Results.Add(new Result(OverwrittenSentencesName, new IntValue(0)));
421      Results.Add(new Result(AverageSentenceComplexityName, new DoubleValue(1.0)));
422      Results.Add(new Result(ExpansionsPerSecondName, "In Thousand expansions per second", new IntValue(0)));
423    }
424
425    // Update the view for intermediate results in an algorithm run.
426    private int updates;
427    private void UpdateView(bool force = false) {
428      updates++;
429
430      if (force || updates % GuiUpdateInterval == 1) {
431        ((IntValue)Results[GeneratedPhrasesName].Value).Value = ArchivedPhrases.Count;
432        ((IntValue)Results[SearchStructureSizeName].Value).Value = OpenPhrases.Count;
433        ((IntValue)Results[GeneratedSentencesName].Value).Value = AllGeneratedSentencesCount;
434        ((IntValue)Results[DistinctSentencesName].Value).Value = DistinctSentencesComplexity.Count;
435        ((IntValue)Results[PhraseExpansionsName].Value).Value = PhraseExpansionCount;
436        ((DoubleValue)Results[AverageSentenceComplexityName].Value).Value = DistinctSentencesComplexity.Count > 0
437          ? DistinctSentencesComplexity.Select(pair => pair.Value).Average()
438          : 0;
439        ((IntValue)Results[OverwrittenSentencesName].Value).Value = OverwrittenSentencesCount;
440        ((IntValue)Results[ExpansionsPerSecondName].Value).Value = (int)((PhraseExpansionCount /
441                                                                          ExecutionTime.TotalSeconds) / 1000.0);
442      }
443    }
444    #endregion
445
446    #region events
447
448    // private event handlers for analyzers
449    private void Analyzers_CheckedItemsChanged(object sender, CollectionItemsChangedEventArgs<IGrammarEnumerationAnalyzer> args) {
450      // newly added items
451      foreach (var item in args.Items.Except(args.OldItems).Union(args.OldItems.Except(args.Items))) {
452        if (Analyzers.ItemChecked(item)) {
453          item.Register(this);
454        } else {
455          item.Deregister(this);
456        }
457      }
458    }
459
460    public event EventHandler<PhraseEventArgs> PhraseFetched;
461    private void OnPhraseFetched(int hash, SymbolList phrase) {
462      if (PhraseFetched != null) {
463        PhraseFetched(this, new PhraseEventArgs(hash, phrase));
464      }
465    }
466
467    public event EventHandler<PhraseAddedEventArgs> PhraseDerived;
468    private void OnPhraseDerived(int parentHash, SymbolList parentSymbolList, int addedHash, SymbolList addedSymbolList, Symbol expandedSymbol, SymbolList expandedProduction) {
469      if (PhraseDerived != null) {
470        PhraseDerived(this, new PhraseAddedEventArgs(parentHash, parentSymbolList, addedHash, addedSymbolList, expandedSymbol, expandedProduction));
471      }
472    }
473
474    public event EventHandler<PhraseAddedEventArgs> SentenceGenerated;
475    private void OnSentenceGenerated(int parentHash, SymbolList parentSymbolList, int addedHash, SymbolList addedSymbolList, Symbol expandedSymbol, SymbolList expandedProduction) {
476      if (SentenceGenerated != null) {
477        SentenceGenerated(this, new PhraseAddedEventArgs(parentHash, parentSymbolList, addedHash, addedSymbolList, expandedSymbol, expandedProduction));
478      }
479    }
480
481    public event EventHandler<PhraseAddedEventArgs> DistinctSentenceGenerated;
482    private void OnDistinctSentenceGenerated(int parentHash, SymbolList parentSymbolList, int addedHash, SymbolList addedSymbolList, Symbol expandedSymbol, SymbolList expandedProduction) {
483      if (DistinctSentenceGenerated != null) {
484        DistinctSentenceGenerated(this, new PhraseAddedEventArgs(parentHash, parentSymbolList, addedHash, addedSymbolList, expandedSymbol, expandedProduction));
485      }
486    }
487
488    #endregion
489
490  }
491
492  #region events for analysis
493
494  public class PhraseEventArgs : EventArgs {
495    public int Hash { get; }
496
497    public SymbolList Phrase { get; }
498
499    public PhraseEventArgs(int hash, SymbolList phrase) {
500      Hash = hash;
501      Phrase = phrase;
502    }
503  }
504
505  public class PhraseAddedEventArgs : EventArgs {
506    public int ParentHash { get; }
507    public int NewHash { get; }
508
509    public SymbolList ParentPhrase { get; }
510    public SymbolList NewPhrase { get; }
511
512    public Symbol ExpandedSymbol { get; }
513
514    public SymbolList ExpandedProduction { get; }
515
516    public PhraseAddedEventArgs(int parentHash, SymbolList parentPhrase, int newHash, SymbolList newPhrase, Symbol expandedSymbol, SymbolList expandedProduction) {
517      ParentHash = parentHash;
518      ParentPhrase = parentPhrase;
519      NewHash = newHash;
520      NewPhrase = newPhrase;
521      ExpandedSymbol = expandedSymbol;
522      ExpandedProduction = expandedProduction;
523    }
524  }
525
526  #endregion
527}
Note: See TracBrowser for help on using the repository browser.