Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 15974 was 15974, checked in by bburlacu, 6 years ago

#2886: implement LRU cache for storing search nodes, introduce SortedSet for handling priorities, fix serialization and cloning

File size: 25.8 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 VariableImportanceWeightName = "Variable Importance Weight";
24    private readonly string SearchStructureSizeName = "Search Structure Size";
25    private readonly string GeneratedPhrasesName = "Generated/Archived Phrases";
26    private readonly string GeneratedSentencesName = "Generated Sentences";
27    private readonly string DistinctSentencesName = "Distinct Sentences";
28    private readonly string PhraseExpansionsName = "Phrase Expansions";
29    private readonly string AverageSentenceComplexityName = "Avg. Sentence Complexity among Distinct";
30    private readonly string OverwrittenSentencesName = "Sentences overwritten";
31    private readonly string AnalyzersParameterName = "Analyzers";
32    private readonly string ExpansionsPerSecondName = "Expansions per second";
33
34    private readonly string OptimizeConstantsParameterName = "Optimize Constants";
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 SearchCacheSizeParameterName = "Search Cache Size";
41    private readonly string SearchDataStructureSizeParameterName = "Search Data Structure Size";
42
43    // result names
44    public static readonly string BestTrainingModelResultName = "Best model (Training)";
45    public static readonly string BestTrainingSolutionResultName = "Best solution (Training)";
46    public static readonly string BestComplexityResultName = "Best solution complexity";
47
48    public override bool SupportsPause { get { return true; } }
49
50    protected IFixedValueParameter<DoubleValue> VariableImportanceWeightParameter {
51      get { return (IFixedValueParameter<DoubleValue>)Parameters[VariableImportanceWeightName]; }
52    }
53
54    protected double VariableImportanceWeight {
55      get { return VariableImportanceWeightParameter.Value.Value; }
56    }
57
58    protected IFixedValueParameter<BoolValue> OptimizeConstantsParameter {
59      get { return (IFixedValueParameter<BoolValue>)Parameters[OptimizeConstantsParameterName]; }
60    }
61
62    public bool OptimizeConstants {
63      get { return OptimizeConstantsParameter.Value.Value; }
64      set { OptimizeConstantsParameter.Value.Value = value; }
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 IFixedValueParameter<IntValue> SearchCacheSizeParameter {
107      get { return (IFixedValueParameter<IntValue>)Parameters[SearchCacheSizeParameterName]; }
108    }
109
110    public int SearchCacheSize {
111      get { return SearchCacheSizeParameter.Value.Value; }
112    }
113
114    public StorageType SearchDataStructure {
115      get { return SearchDataStructureParameter.Value.Value; }
116      set { SearchDataStructureParameter.Value.Value = value; }
117    }
118
119    public IFixedValueParameter<ReadOnlyCheckedItemCollection<IGrammarEnumerationAnalyzer>> AnalyzersParameter {
120      get { return (IFixedValueParameter<ReadOnlyCheckedItemCollection<IGrammarEnumerationAnalyzer>>)Parameters[AnalyzersParameterName]; }
121    }
122
123    public ICheckedItemCollection<IGrammarEnumerationAnalyzer> Analyzers {
124      get { return AnalyzersParameter.Value; }
125    }
126
127    public IFixedValueParameter<ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>>> GrammarSymbolsParameter {
128      get { return (IFixedValueParameter<ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>>>)Parameters[GrammarSymbolsParameterName]; }
129    }
130
131    public ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>> GrammarSymbols {
132      get { return GrammarSymbolsParameter.Value; }
133    }
134
135    public SymbolString BestTrainingSentence { get; set; }     // Currently set in RSquaredEvaluator: quite hacky, but makes testing much easier for now...
136    #endregion
137
138    [Storable]
139    public Dictionary<int, int> DistinctSentencesComplexity { get; private set; }  // Semantically distinct sentences and their length in a run.
140
141    [Storable]
142    public HashSet<int> ArchivedPhrases { get; private set; }
143
144    [Storable]
145    internal SearchDataStore OpenPhrases { get; private set; }           // Stack/Queue/etc. for fetching the next node in the search tree. 
146
147    [StorableHook(HookType.AfterDeserialization)]
148    private void AfterDeserialization() {
149      variableImportance = CalculateVariableImportances();
150    }
151
152    #region execution stats
153    [Storable]
154    public int AllGeneratedSentencesCount { get; private set; }
155
156    [Storable]
157    public int OverwrittenSentencesCount { get; private set; } // It is not guaranteed that shorter solutions are found first.
158                                                               // When longer solutions are overwritten with shorter ones,
159                                                               // this counter is increased.
160    [Storable]
161    public int PhraseExpansionCount { get; private set; }      // Number, how many times a nonterminal symbol is replaced with a production rule.
162    #endregion
163
164    public Grammar Grammar { get; private set; }
165
166    #region ctors
167    public override IDeepCloneable Clone(Cloner cloner) {
168      return new GrammarEnumerationAlgorithm(this, cloner);
169    }
170
171    public GrammarEnumerationAlgorithm() {
172      Parameters.Add(new FixedValueParameter<DoubleValue>(VariableImportanceWeightName, "Variable Weight.", new DoubleValue(1.0)));
173      Parameters.Add(new FixedValueParameter<BoolValue>(OptimizeConstantsParameterName, "Run constant optimization in sentence evaluation.", new BoolValue(false)));
174      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)));
175      Parameters.Add(new FixedValueParameter<IntValue>(MaxComplexityParameterName, "The maximum number of variable symbols in a sentence.", new IntValue(12)));
176      Parameters.Add(new FixedValueParameter<IntValue>(GuiUpdateIntervalParameterName, "Number of generated sentences, until GUI is refreshed.", new IntValue(5000)));
177      Parameters.Add(new FixedValueParameter<IntValue>(SearchCacheSizeParameterName, "The size of the search node cache.", new IntValue((int)1e5)));
178      Parameters.Add(new FixedValueParameter<IntValue>(SearchDataStructureSizeParameterName, "The size of the search data structure.", new IntValue((int)1e5)));
179      Parameters.Add(new FixedValueParameter<EnumValue<StorageType>>(SearchDataStructureParameterName, new EnumValue<StorageType>(StorageType.PriorityQueue)));
180
181      SearchDataStructureParameter.Value.ValueChanged += (o, e) => Prepare();
182      SearchDataStructureSizeParameter.Value.ValueChanged += (o, e) => Prepare();
183      SearchCacheSizeParameter.Value.ValueChanged += (o, e) => Prepare();
184
185      var availableAnalyzers = new IGrammarEnumerationAnalyzer[] {
186        new SearchGraphVisualizer(),
187        new SentenceLogger(),
188        new RSquaredEvaluator()
189      };
190
191      Parameters.Add(new FixedValueParameter<ReadOnlyCheckedItemCollection<IGrammarEnumerationAnalyzer>>(
192        AnalyzersParameterName,
193        new CheckedItemCollection<IGrammarEnumerationAnalyzer>(availableAnalyzers).AsReadOnly()));
194
195      Analyzers.CheckedItemsChanged += Analyzers_CheckedItemsChanged;
196
197      foreach (var analyzer in Analyzers) {
198        Analyzers.SetItemCheckedState(analyzer, false);
199      }
200      Analyzers.SetItemCheckedState(Analyzers.First(analyzer => analyzer is RSquaredEvaluator), true);
201
202      var grammarSymbols = Enum.GetValues(typeof(GrammarRule))
203        .Cast<GrammarRule>()
204        .Select(v => new EnumValue<GrammarRule>(v));
205
206      Parameters.Add(new FixedValueParameter<ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>>>(
207        GrammarSymbolsParameterName,
208        new ReadOnlyCheckedItemCollection<EnumValue<GrammarRule>>(new CheckedItemCollection<EnumValue<GrammarRule>>(grammarSymbols))
209      ));
210      foreach (EnumValue<GrammarRule> grammarSymbol in GrammarSymbols) {
211        GrammarSymbols.SetItemCheckedState(grammarSymbol, true);
212      }
213
214      // set a default problem
215      Problem = new RegressionProblem() {
216        ProblemData = new Problems.Instances.DataAnalysis.PolyTen(seed: 1234).GenerateRegressionData()
217      };
218    }
219
220    public GrammarEnumerationAlgorithm(GrammarEnumerationAlgorithm original, Cloner cloner) : base(original, cloner) {
221      foreach (var analyzer in Analyzers.CheckedItems)
222        analyzer.Register(this);
223      Analyzers.CheckedItemsChanged += Analyzers_CheckedItemsChanged;
224
225      DistinctSentencesComplexity = new Dictionary<int, int>(original.DistinctSentencesComplexity);
226      ArchivedPhrases = new HashSet<int>(original.ArchivedPhrases);
227      OpenPhrases = cloner.Clone(original.OpenPhrases);
228      Grammar = cloner.Clone(original.Grammar);
229
230      AllGeneratedSentencesCount = original.AllGeneratedSentencesCount;
231      OverwrittenSentencesCount = original.OverwrittenSentencesCount;
232      PhraseExpansionCount = original.PhraseExpansionCount;
233
234      if (original.variableImportance != null)
235        variableImportance = new Dictionary<VariableTerminalSymbol, double>(original.variableImportance);
236    }
237    #endregion
238
239    private Dictionary<VariableTerminalSymbol, double> variableImportance;
240
241    public override void Prepare() {
242      DistinctSentencesComplexity = new Dictionary<int, int>();
243      ArchivedPhrases = new HashSet<int>();
244      AllGeneratedSentencesCount = 0;
245      OverwrittenSentencesCount = 0;
246      PhraseExpansionCount = 0;
247
248      Analyzers.OfType<RSquaredEvaluator>().First().OptimizeConstants = OptimizeConstants;
249      Grammar = new Grammar(Problem.ProblemData.AllowedInputVariables.ToArray(), GrammarSymbols.CheckedItems.Select(v => v.Value));
250      OpenPhrases = new SearchDataStore(SearchDataStructure, SearchDataStructureSize, SearchCacheSize); // Select search strategy
251
252      base.Prepare(); // this actually clears the results which will get reinitialized on Run()
253    }
254
255    private Dictionary<VariableTerminalSymbol, double> CalculateVariableImportances() {
256      variableImportance = new Dictionary<VariableTerminalSymbol, double>();
257
258      RandomForestRegression rf = new RandomForestRegression();
259      rf.Problem = Problem;
260      rf.Start();
261      IRegressionSolution rfSolution = (RandomForestRegressionSolution)rf.Results["Random forest regression solution"].Value;
262      var rfImpacts = RegressionSolutionVariableImpactsCalculator.CalculateImpacts(
263        rfSolution,
264        RegressionSolutionVariableImpactsCalculator.DataPartitionEnum.Training,
265        RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum.Shuffle);
266
267      // save the normalized importances
268      var sum = rfImpacts.Sum(x => x.Item2);
269      foreach (Tuple<string, double> rfImpact in rfImpacts) {
270        VariableTerminalSymbol varSym = Grammar.VarTerminals.First(v => v.StringRepresentation == rfImpact.Item1);
271        variableImportance[varSym] = rfImpact.Item2 / sum;
272      }
273      return variableImportance;
274    }
275
276    protected override void Run(CancellationToken cancellationToken) {
277      // do not reinitialize the algorithm if we're resuming from pause
278      if (previousExecutionState != ExecutionState.Paused) {
279        CalculateVariableImportances();
280        InitResults();
281        var phrase0 = new SymbolString(new[] { Grammar.StartSymbol });
282        var phrase0Hash = Grammar.Hasher.CalcHashCode(phrase0);
283
284        OpenPhrases.Store(new SearchNode(phrase0Hash, 0.0, 0.0, phrase0));
285      }
286
287      int maxSentenceLength = GetMaxSentenceLength();
288      var errorWeight = ErrorWeight;
289      var variableImportanceWeight = VariableImportanceWeight;
290      // main search loop
291      while (OpenPhrases.Count > 0) {
292        if (cancellationToken.IsCancellationRequested)
293          break;
294
295        SearchNode fetchedSearchNode = OpenPhrases.GetNext();
296
297        if (fetchedSearchNode == null)
298          continue;
299
300        SymbolString currPhrase = fetchedSearchNode.SymbolString;
301
302        OnPhraseFetched(fetchedSearchNode.Hash, currPhrase);
303
304        ArchivedPhrases.Add(fetchedSearchNode.Hash);
305
306        // expand next nonterminal symbols
307        int nonterminalSymbolIndex = currPhrase.NextNonterminalIndex();
308        NonterminalSymbol expandedSymbol = (NonterminalSymbol)currPhrase[nonterminalSymbolIndex];
309        var appliedProductions = Grammar.Productions[expandedSymbol];
310
311        for (int i = 0; i < appliedProductions.Count; i++) {
312          PhraseExpansionCount++;
313
314          SymbolString newPhrase = currPhrase.DerivePhrase(nonterminalSymbolIndex, appliedProductions[i]);
315          int newPhraseComplexity = Grammar.GetComplexity(newPhrase);
316
317          if (newPhraseComplexity > MaxComplexity)
318            continue;
319
320          var phraseHash = Grammar.Hasher.CalcHashCode(newPhrase);
321
322          OnPhraseDerived(fetchedSearchNode.Hash, fetchedSearchNode.SymbolString, phraseHash, newPhrase, expandedSymbol, appliedProductions[i]);
323
324          if (newPhrase.IsSentence()) {
325            AllGeneratedSentencesCount++;
326
327            OnSentenceGenerated(fetchedSearchNode.Hash, fetchedSearchNode.SymbolString, phraseHash, newPhrase, expandedSymbol, appliedProductions[i]);
328
329            // Is the best solution found? (only if RSquaredEvaluator is activated)
330            if (Results.ContainsKey(RSquaredEvaluator.BestTrainingQualityResultName)) {
331              double r2 = ((DoubleValue)Results[RSquaredEvaluator.BestTrainingQualityResultName].Value).Value;
332              if (r2.IsAlmost(1.0)) {
333                UpdateView(force: true);
334                return;
335              }
336            }
337
338            if (!DistinctSentencesComplexity.ContainsKey(phraseHash) || DistinctSentencesComplexity[phraseHash] > newPhraseComplexity) {
339              if (DistinctSentencesComplexity.ContainsKey(phraseHash)) OverwrittenSentencesCount++; // for analysis only
340
341              DistinctSentencesComplexity[phraseHash] = newPhraseComplexity;
342              OnDistinctSentenceGenerated(fetchedSearchNode.Hash, fetchedSearchNode.SymbolString, phraseHash, newPhrase, expandedSymbol, appliedProductions[i]);
343            }
344            UpdateView();
345
346          } else if (!OpenPhrases.Contains(phraseHash) && !ArchivedPhrases.Contains(phraseHash)) {
347
348            double r2 = GetR2(newPhrase, fetchedSearchNode.R2);
349            double phrasePriority = GetPriority(newPhrase, r2, maxSentenceLength, errorWeight, variableImportanceWeight);
350
351            SearchNode newSearchNode = new SearchNode(phraseHash, phrasePriority, r2, newPhrase);
352            OpenPhrases.Store(newSearchNode);
353          }
354        }
355      }
356      UpdateView(force: true);
357    }
358
359    protected double GetPriority(SymbolString phrase, double r2, int maxSentenceLength, double errorWeight, double variableImportanceWeight) {
360      var distinctVars = phrase.OfType<VariableTerminalSymbol>().Distinct();
361
362      var sum = 0d;
363      foreach (var v in distinctVars) {
364        sum += variableImportance[v];
365      }
366      var phraseVariableImportance = 1 - sum;
367
368      double relLength = (double)phrase.Count() / maxSentenceLength;
369      double error = 1.0 - r2;
370
371      return relLength + errorWeight * error + variableImportanceWeight * phraseVariableImportance;
372    }
373
374    private double GetR2(SymbolString phrase, double parentR2) {
375      int length = phrase.Count();
376
377      // If the only nonterminal symbol is Expr, we can need to evaluate the sentence. Otherwise
378      // the phrase has the same r2 as its parent, from which it was derived.
379      for (int i = 0; i < length; i++) {
380        if (phrase[i] is NonterminalSymbol && phrase[i] != Grammar.Expr) {
381          return parentR2;
382        }
383      }
384
385      return Grammar.EvaluatePhrase(phrase, Problem.ProblemData, OptimizeConstants);
386    }
387
388    private int GetMaxSentenceLength() {
389      SymbolString s = new SymbolString(Grammar.StartSymbol);
390
391      while (!s.IsSentence() && Grammar.GetComplexity(s) <= MaxComplexity) {
392        int expandedSymbolIndex = s.NextNonterminalIndex();
393        NonterminalSymbol expandedSymbol = (NonterminalSymbol)s[expandedSymbolIndex];
394
395        var productions = Grammar.Productions[expandedSymbol];
396        var longestProduction = productions // Find production with most terminal symbols to expand as much as possible...
397          .OrderBy(CountTerminals)          // but with lowest complexity/nonterminal count to keep complexity low.                                                                                     
398          .ThenByDescending(CountNonTerminals)
399          .First();
400
401        s = s.DerivePhrase(expandedSymbolIndex, longestProduction);
402      }
403
404      return s.Count();
405    }
406
407    private int CountTerminals(Production p) {
408      return p.Count(s => s is TerminalSymbol);
409    }
410
411    private int CountNonTerminals(Production p) {
412      return p.Count(s => s is NonterminalSymbol);
413    }
414
415    #region pause support
416    private ExecutionState previousExecutionState;
417    protected override void OnPaused() {
418      previousExecutionState = this.ExecutionState;
419      base.OnPaused();
420    }
421    protected override void OnPrepared() {
422      previousExecutionState = this.ExecutionState;
423      base.OnPrepared();
424    }
425    protected override void OnStarted() {
426      previousExecutionState = this.ExecutionState;
427      base.OnStarted();
428    }
429    protected override void OnStopped() {
430      previousExecutionState = this.ExecutionState;
431
432      if (BestTrainingSentence == null) {
433        base.OnStopped();
434        return;
435      }
436
437      var tree = Grammar.ParseSymbolicExpressionTree(BestTrainingSentence);
438      var model = new SymbolicRegressionModel(Problem.ProblemData.TargetVariable, tree, new SymbolicDataAnalysisExpressionTreeLinearInterpreter());
439      model.Scale(Problem.ProblemData);
440      var bestTrainingSolution = new SymbolicRegressionSolution(model, Problem.ProblemData);
441      Results.AddOrUpdateResult(BestTrainingModelResultName, model);
442      Results.AddOrUpdateResult(BestTrainingSolutionResultName, bestTrainingSolution);
443      Results.AddOrUpdateResult(BestComplexityResultName, new IntValue(Grammar.GetComplexity(BestTrainingSentence)));
444      base.OnStopped();
445    }
446    #endregion
447
448    #region Visualization in HL
449    // Initialize entries in result set.
450    private void InitResults() {
451      Results.Clear();
452      Results.Add(new Result(GeneratedPhrasesName, new IntValue(0)));
453      Results.Add(new Result(SearchStructureSizeName, new IntValue(0)));
454      Results.Add(new Result(GeneratedSentencesName, new IntValue(0)));
455      Results.Add(new Result(DistinctSentencesName, new IntValue(0)));
456      Results.Add(new Result(PhraseExpansionsName, new IntValue(0)));
457      Results.Add(new Result(OverwrittenSentencesName, new IntValue(0)));
458      Results.Add(new Result(AverageSentenceComplexityName, new DoubleValue(1.0)));
459      Results.Add(new Result(ExpansionsPerSecondName, "In Thousand expansions per second", new IntValue(0)));
460    }
461
462    // Update the view for intermediate results in an algorithm run.
463    private int updates;
464    private void UpdateView(bool force = false) {
465      updates++;
466
467      if (force || updates % GuiUpdateInterval == 1) {
468        ((IntValue)Results[GeneratedPhrasesName].Value).Value = ArchivedPhrases.Count;
469        ((IntValue)Results[SearchStructureSizeName].Value).Value = OpenPhrases.Count;
470        ((IntValue)Results[GeneratedSentencesName].Value).Value = AllGeneratedSentencesCount;
471        ((IntValue)Results[DistinctSentencesName].Value).Value = DistinctSentencesComplexity.Count;
472        ((IntValue)Results[PhraseExpansionsName].Value).Value = PhraseExpansionCount;
473        ((DoubleValue)Results[AverageSentenceComplexityName].Value).Value = DistinctSentencesComplexity.Count > 0
474          ? DistinctSentencesComplexity.Select(pair => pair.Value).Average()
475          : 0;
476        ((IntValue)Results[OverwrittenSentencesName].Value).Value = OverwrittenSentencesCount;
477        ((IntValue)Results[ExpansionsPerSecondName].Value).Value = (int)((PhraseExpansionCount /
478                                                                          ExecutionTime.TotalSeconds) / 1000.0);
479      }
480    }
481    #endregion
482
483    #region events
484
485    // private event handlers for analyzers
486    private void Analyzers_CheckedItemsChanged(object sender, CollectionItemsChangedEventArgs<IGrammarEnumerationAnalyzer> args) {
487      // newly added items
488      foreach (var item in args.Items.Except(args.OldItems).Union(args.OldItems.Except(args.Items))) {
489        if (Analyzers.ItemChecked(item)) {
490          item.Register(this);
491        } else {
492          item.Deregister(this);
493        }
494      }
495    }
496
497    public event EventHandler<PhraseEventArgs> PhraseFetched;
498    private void OnPhraseFetched(int hash, SymbolString symbolString) {
499      if (PhraseFetched != null) {
500        PhraseFetched(this, new PhraseEventArgs(hash, symbolString));
501      }
502    }
503
504    public event EventHandler<PhraseAddedEventArgs> PhraseDerived;
505    private void OnPhraseDerived(int parentHash, SymbolString parentSymbolString, int addedHash, SymbolString addedSymbolString, Symbol expandedSymbol, Production expandedProduction) {
506      if (PhraseDerived != null) {
507        PhraseDerived(this, new PhraseAddedEventArgs(parentHash, parentSymbolString, addedHash, addedSymbolString, expandedSymbol, expandedProduction));
508      }
509    }
510
511    public event EventHandler<PhraseAddedEventArgs> SentenceGenerated;
512    private void OnSentenceGenerated(int parentHash, SymbolString parentSymbolString, int addedHash, SymbolString addedSymbolString, Symbol expandedSymbol, Production expandedProduction) {
513      if (SentenceGenerated != null) {
514        SentenceGenerated(this, new PhraseAddedEventArgs(parentHash, parentSymbolString, addedHash, addedSymbolString, expandedSymbol, expandedProduction));
515      }
516    }
517
518    public event EventHandler<PhraseAddedEventArgs> DistinctSentenceGenerated;
519    private void OnDistinctSentenceGenerated(int parentHash, SymbolString parentSymbolString, int addedHash, SymbolString addedSymbolString, Symbol expandedSymbol, Production expandedProduction) {
520      if (DistinctSentenceGenerated != null) {
521        DistinctSentenceGenerated(this, new PhraseAddedEventArgs(parentHash, parentSymbolString, addedHash, addedSymbolString, expandedSymbol, expandedProduction));
522      }
523    }
524
525    #endregion
526
527  }
528
529  #region events for analysis
530
531  public class PhraseEventArgs : EventArgs {
532    public int Hash { get; }
533
534    public SymbolString Phrase { get; }
535
536    public PhraseEventArgs(int hash, SymbolString phrase) {
537      Hash = hash;
538      Phrase = phrase;
539    }
540  }
541
542  public class PhraseAddedEventArgs : EventArgs {
543    public int ParentHash { get; }
544    public int NewHash { get; }
545
546    public SymbolString ParentPhrase { get; }
547    public SymbolString NewPhrase { get; }
548
549    public Symbol ExpandedSymbol { get; }
550
551    public Production ExpandedProduction { get; }
552
553    public PhraseAddedEventArgs(int parentHash, SymbolString parentPhrase, int newHash, SymbolString newPhrase, Symbol expandedSymbol, Production expandedProduction) {
554      ParentHash = parentHash;
555      ParentPhrase = parentPhrase;
556      NewHash = newHash;
557      NewPhrase = newPhrase;
558      ExpandedSymbol = expandedSymbol;
559      ExpandedProduction = expandedProduction;
560    }
561  }
562
563  #endregion
564}
Note: See TracBrowser for help on using the repository browser.