Free cookie consent management tool by TermsFeed Policy Generator

source: branches/GP-Refactoring-713/sources/HeuristicLab.GP.StructureIdentification/3.3/AlgorithmBase.cs @ 2212

Last change on this file since 2212 was 2212, checked in by gkronber, 15 years ago

GP Refactoring: #713

  • added project GP.Operators
  • moved operators from plugin GP to plugin GP.Operators
  • deleted unused constraints
  • removed dependency of GP plugins on Constraints plugin
  • moved StructID functions into directory Symbols
  • deleted unused class FunView
  • implemented add and remove functionality for the FunctionLibraryView
File size: 22.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Xml;
25using HeuristicLab.Core;
26using HeuristicLab.Data;
27using HeuristicLab.DataAnalysis;
28using HeuristicLab.Evolutionary;
29using HeuristicLab.GP.Interfaces;
30using HeuristicLab.Logging;
31using HeuristicLab.Modeling;
32using HeuristicLab.Operators;
33using HeuristicLab.Random;
34using HeuristicLab.Selection;
35
36namespace HeuristicLab.GP.StructureIdentification {
37  public abstract class AlgorithmBase : ItemBase, IAlgorithm, IStochasticAlgorithm {
38    public virtual string Name { get { return "GP"; } }
39    public virtual string Description { get { return "TODO"; } }
40
41    public abstract Dataset Dataset { get; set; }
42    public abstract int TargetVariable { get; set; }
43
44    public virtual double MutationRate {
45      get { return GetVariableInjector().GetVariable("MutationRate").GetValue<DoubleData>().Data; }
46      set { GetVariableInjector().GetVariable("MutationRate").GetValue<DoubleData>().Data = value; }
47    }
48    public virtual int PopulationSize {
49      get { return GetVariableInjector().GetVariable("PopulationSize").GetValue<IntData>().Data; }
50      set { GetVariableInjector().GetVariable("PopulationSize").GetValue<IntData>().Data = value; }
51    }
52
53    public virtual bool SetSeedRandomly {
54      get { return GetRandomInjector().GetVariable("SetSeedRandomly").GetValue<BoolData>().Data; }
55      set { GetRandomInjector().GetVariable("SetSeedRandomly").GetValue<BoolData>().Data = value; }
56    }
57
58    public virtual int RandomSeed {
59      get { return GetRandomInjector().GetVariable("Seed").GetValue<IntData>().Data; }
60      set { GetRandomInjector().GetVariable("Seed").GetValue<IntData>().Data = value; }
61    }
62
63    public virtual IOperator ProblemInjector {
64      get { return algorithm.SubOperators[1]; }
65      set {
66        value.Name = "ProblemInjector";
67        algorithm.RemoveSubOperator(1);
68        algorithm.AddSubOperator(value, 1);
69      }
70    }
71
72    public virtual IModel Model {
73      get {
74        if (!engine.Terminated) throw new InvalidOperationException("The algorithm is still running. Wait until the algorithm is terminated to retrieve the result.");
75        IScope bestModelScope = engine.GlobalScope.GetVariableValue<IScope>("BestValidationSolution", false);
76        return CreateGPModel(bestModelScope);
77      }
78    }
79
80    public virtual int Elites {
81      get { return GetVariableInjector().GetVariable("Elites").GetValue<IntData>().Data; }
82      set { GetVariableInjector().GetVariable("Elites").GetValue<IntData>().Data = value; }
83    }
84
85    public virtual int MaxTreeSize {
86      get { return GetVariableInjector().GetVariable("MaxTreeSize").GetValue<IntData>().Data; }
87      set { GetVariableInjector().GetVariable("MaxTreeSize").GetValue<IntData>().Data = value; }
88    }
89
90    public virtual int MaxTreeHeight {
91      get { return GetVariableInjector().GetVariable("MaxTreeHeight").GetValue<IntData>().Data; }
92      set { GetVariableInjector().GetVariable("MaxTreeHeight").GetValue<IntData>().Data = value; }
93    }
94
95    public virtual int Parents {
96      get { return GetVariableInjector().GetVariable("Parents").GetValue<IntData>().Data; }
97      set { GetVariableInjector().GetVariable("Parents").GetValue<IntData>().Data = value; }
98    }
99
100    public virtual double PunishmentFactor {
101      get { return GetVariableInjector().GetVariable("PunishmentFactor").GetValue<DoubleData>().Data; }
102      set { GetVariableInjector().GetVariable("PunishmentFactor").GetValue<DoubleData>().Data = value; }
103    }
104
105    public virtual bool UseEstimatedTargetValue {
106      get { return GetVariableInjector().GetVariable("UseEstimatedTargetValue").GetValue<BoolData>().Data; }
107      set { GetVariableInjector().GetVariable("UseEstimatedTargetValue").GetValue<BoolData>().Data = value; }
108    }
109
110    private IOperator algorithm;
111
112    private SequentialEngine.SequentialEngine engine;
113    public IEngine Engine {
114      get { return engine; }
115      protected set { engine = (SequentialEngine.SequentialEngine)value; }
116    }
117
118    public AlgorithmBase() {
119      engine = new SequentialEngine.SequentialEngine();
120      CombinedOperator algo = CreateAlgorithm();
121      engine.OperatorGraph.AddOperator(algo);
122      engine.OperatorGraph.InitialOperator = algo;
123      SetSeedRandomly = true;
124      Elites = 1;
125      MutationRate = 0.15;
126      PopulationSize = 1000;
127      MaxTreeSize = 100;
128      MaxTreeHeight = 10;
129      Parents = 2000;
130      PunishmentFactor = 10;
131      UseEstimatedTargetValue = false;
132    }
133
134    protected internal virtual CombinedOperator CreateAlgorithm() {
135      CombinedOperator algo = new CombinedOperator();
136      algo.Name = "GP";
137      SequentialProcessor seq = new SequentialProcessor();
138      IOperator problemInjector = CreateProblemInjector();
139
140      RandomInjector randomInjector = new RandomInjector();
141      randomInjector.Name = "Random Injector";
142
143      IOperator globalInjector = CreateGlobalInjector();
144      IOperator initialization = CreateInitialization();
145      IOperator funLibInjector = CreateFunctionLibraryInjector();
146
147      IOperator mainLoop = CreateMainLoop();
148      mainLoop.Name = "Main loop";
149
150      IOperator treeCreator = CreateTreeCreator();
151
152      MeanSquaredErrorEvaluator evaluator = new MeanSquaredErrorEvaluator();
153      evaluator.GetVariableInfo("MSE").ActualName = "Quality";
154      evaluator.GetVariableInfo("SamplesStart").ActualName = "ActualTrainingSamplesStart";
155      evaluator.GetVariableInfo("SamplesEnd").ActualName = "ActualTrainingSamplesEnd";
156      evaluator.Name = "Evaluator";
157
158      IOperator crossover = CreateCrossover();
159      IOperator manipulator = CreateManipulator();
160
161      IOperator selector = CreateSelector();
162      LeftReducer cleanUp = new LeftReducer();
163
164      seq.AddSubOperator(randomInjector);
165      seq.AddSubOperator(problemInjector);
166      seq.AddSubOperator(globalInjector);
167      seq.AddSubOperator(funLibInjector);
168      seq.AddSubOperator(initialization);
169      seq.AddSubOperator(mainLoop);
170      seq.AddSubOperator(cleanUp);
171
172      initialization.AddSubOperator(treeCreator);
173      initialization.AddSubOperator(evaluator);
174
175      mainLoop.AddSubOperator(selector);
176      mainLoop.AddSubOperator(crossover);
177      mainLoop.AddSubOperator(manipulator);
178      mainLoop.AddSubOperator(evaluator);
179      algo.OperatorGraph.AddOperator(seq);
180      algo.OperatorGraph.InitialOperator = seq;
181      this.algorithm = seq;
182      return algo;
183    }
184
185    protected internal virtual IOperator CreateProblemInjector() {
186      return new EmptyOperator();
187    }
188
189    protected internal abstract IOperator CreateSelector();
190
191    protected internal abstract IOperator CreateCrossover();
192
193    protected internal abstract IOperator CreateTreeCreator();
194
195    protected internal abstract IOperator CreateFunctionLibraryInjector();
196
197    protected internal virtual IOperator CreateGlobalInjector() {
198      VariableInjector injector = new VariableInjector();
199      injector.Name = "Global Injector";
200      injector.AddVariable(new HeuristicLab.Core.Variable("Generations", new IntData(0)));
201      injector.AddVariable(new HeuristicLab.Core.Variable("MutationRate", new DoubleData()));
202      injector.AddVariable(new HeuristicLab.Core.Variable("PopulationSize", new IntData()));
203      injector.AddVariable(new HeuristicLab.Core.Variable("Elites", new IntData()));
204      injector.AddVariable(new HeuristicLab.Core.Variable("Maximization", new BoolData(false)));
205      injector.AddVariable(new HeuristicLab.Core.Variable("MaxTreeHeight", new IntData()));
206      injector.AddVariable(new HeuristicLab.Core.Variable("MaxTreeSize", new IntData()));
207      injector.AddVariable(new HeuristicLab.Core.Variable("EvaluatedSolutions", new IntData(0)));
208      injector.AddVariable(new HeuristicLab.Core.Variable("TotalEvaluatedNodes", new DoubleData(0)));
209      injector.AddVariable(new HeuristicLab.Core.Variable("Parents", new IntData()));
210      injector.AddVariable(new HeuristicLab.Core.Variable("PunishmentFactor", new DoubleData()));
211      injector.AddVariable(new HeuristicLab.Core.Variable("UseEstimatedTargetValue", new BoolData()));
212      injector.AddVariable(new HeuristicLab.Core.Variable("TreeEvaluator", new HL2TreeEvaluator()));
213      return injector;
214    }
215
216    protected internal abstract IOperator CreateManipulator();
217
218    protected internal virtual IOperator CreateInitialization() {
219      CombinedOperator init = new CombinedOperator();
220      init.Name = "Initialization";
221      SequentialProcessor seq = new SequentialProcessor();
222      SubScopesCreater subScopesCreater = new SubScopesCreater();
223      subScopesCreater.GetVariableInfo("SubScopes").ActualName = "PopulationSize";
224      UniformSequentialSubScopesProcessor subScopesProc = new UniformSequentialSubScopesProcessor();
225      SequentialProcessor individualSeq = new SequentialProcessor();
226      OperatorExtractor treeCreater = new OperatorExtractor();
227      treeCreater.Name = "Tree generator (extr.)";
228      treeCreater.GetVariableInfo("Operator").ActualName = "Tree generator";
229      OperatorExtractor evaluator = new OperatorExtractor();
230      evaluator.Name = "Evaluator (extr.)";
231      evaluator.GetVariableInfo("Operator").ActualName = "Evaluator";
232      MeanSquaredErrorEvaluator validationEvaluator = new MeanSquaredErrorEvaluator();
233      validationEvaluator.GetVariableInfo("MSE").ActualName = "ValidationQuality";
234      validationEvaluator.GetVariableInfo("SamplesStart").ActualName = "ValidationSamplesStart";
235      validationEvaluator.GetVariableInfo("SamplesEnd").ActualName = "ValidationSamplesEnd";
236      Counter evalCounter = new Counter();
237      evalCounter.GetVariableInfo("Value").ActualName = "EvaluatedSolutions";
238      Sorter sorter = new Sorter();
239      sorter.GetVariableInfo("Descending").ActualName = "Maximization";
240      sorter.GetVariableInfo("Value").ActualName = "Quality";
241
242      seq.AddSubOperator(subScopesCreater);
243      seq.AddSubOperator(subScopesProc);
244      seq.AddSubOperator(sorter);
245
246      subScopesProc.AddSubOperator(individualSeq);
247      individualSeq.AddSubOperator(treeCreater);
248      individualSeq.AddSubOperator(evaluator);
249      individualSeq.AddSubOperator(validationEvaluator);
250      individualSeq.AddSubOperator(evalCounter);
251
252      init.OperatorGraph.AddOperator(seq);
253      init.OperatorGraph.InitialOperator = seq;
254      return init;
255    }
256
257    protected internal virtual IOperator CreateMainLoop() {
258      CombinedOperator main = new CombinedOperator();
259      SequentialProcessor seq = new SequentialProcessor();
260      IOperator childCreater = CreateChildCreater();
261      IOperator replacement = CreateReplacement();
262
263      BestSolutionStorer solutionStorer = new BestSolutionStorer();
264      solutionStorer.GetVariableInfo("Quality").ActualName = "ValidationQuality";
265      solutionStorer.GetVariableInfo("BestSolution").ActualName = "BestValidationSolution";
266      solutionStorer.AddSubOperator(CreateBestSolutionProcessor());
267
268      BestAverageWorstQualityCalculator qualityCalculator = new BestAverageWorstQualityCalculator();
269      BestAverageWorstQualityCalculator validationQualityCalculator = new BestAverageWorstQualityCalculator();
270      validationQualityCalculator.Name = "BestAverageWorstValidationQualityCalculator";
271      validationQualityCalculator.GetVariableInfo("Quality").ActualName = "ValidationQuality";
272      validationQualityCalculator.GetVariableInfo("BestQuality").ActualName = "BestValidationQuality";
273      validationQualityCalculator.GetVariableInfo("AverageQuality").ActualName = "AverageValidationQuality";
274      validationQualityCalculator.GetVariableInfo("WorstQuality").ActualName = "WorstValidationQuality";
275      IOperator loggingOperator = CreateLoggingOperator();
276      Counter counter = new Counter();
277      counter.GetVariableInfo("Value").ActualName = "Generations";
278      IOperator loopCondition = CreateLoopCondition(seq);
279
280      seq.AddSubOperator(childCreater);
281      seq.AddSubOperator(replacement);
282      seq.AddSubOperator(solutionStorer);
283      seq.AddSubOperator(qualityCalculator);
284      seq.AddSubOperator(validationQualityCalculator);
285      seq.AddSubOperator(loggingOperator);
286      seq.AddSubOperator(counter);
287      seq.AddSubOperator(loopCondition);
288
289      main.OperatorGraph.AddOperator(seq);
290      main.OperatorGraph.InitialOperator = seq;
291      return main;
292    }
293
294    protected internal virtual IOperator CreateLoggingOperator() {
295      return new EmptyOperator();
296    }
297
298    protected internal virtual IOperator CreateLoopCondition(IOperator loop) {
299      SequentialProcessor seq = new SequentialProcessor();
300      seq.Name = "Loop Condition";
301      LessThanComparator comparator = new LessThanComparator();
302      comparator.GetVariableInfo("LeftSide").ActualName = "Generations";
303      comparator.GetVariableInfo("RightSide").ActualName = "MaxGenerations";
304      comparator.GetVariableInfo("Result").ActualName = "GenerationsCondition";
305      ConditionalBranch cond = new ConditionalBranch();
306      cond.GetVariableInfo("Condition").ActualName = "GenerationsCondition";
307
308      seq.AddSubOperator(comparator);
309      seq.AddSubOperator(cond);
310
311      cond.AddSubOperator(loop);
312      return seq;
313    }
314
315    protected internal virtual IOperator CreateBestSolutionProcessor() {
316      return new EmptyOperator();
317    }
318
319    protected internal virtual IOperator CreateReplacement() {
320      CombinedOperator replacement = new CombinedOperator();
321      replacement.Name = "Replacement";
322      SequentialProcessor seq = new SequentialProcessor();
323      SequentialSubScopesProcessor seqScopeProc = new SequentialSubScopesProcessor();
324      SequentialProcessor selectedProc = new SequentialProcessor();
325      LeftSelector leftSelector = new LeftSelector();
326      leftSelector.GetVariableInfo("Selected").ActualName = "Elites";
327      RightReducer rightReducer = new RightReducer();
328
329      SequentialProcessor remainingProc = new SequentialProcessor();
330      RightSelector rightSelector = new RightSelector();
331      rightSelector.GetVariableInfo("Selected").ActualName = "Elites";
332      LeftReducer leftReducer = new LeftReducer();
333      MergingReducer mergingReducer = new MergingReducer();
334      Sorter sorter = new Sorter();
335      sorter.GetVariableInfo("Descending").ActualName = "Maximization";
336      sorter.GetVariableInfo("Value").ActualName = "Quality";
337
338      seq.AddSubOperator(seqScopeProc);
339      seqScopeProc.AddSubOperator(selectedProc);
340      selectedProc.AddSubOperator(leftSelector);
341      selectedProc.AddSubOperator(rightReducer);
342
343      seqScopeProc.AddSubOperator(remainingProc);
344      remainingProc.AddSubOperator(rightSelector);
345      remainingProc.AddSubOperator(leftReducer);
346      seq.AddSubOperator(mergingReducer);
347      seq.AddSubOperator(sorter);
348      replacement.OperatorGraph.AddOperator(seq);
349      replacement.OperatorGraph.InitialOperator = seq;
350      return replacement;
351    }
352
353    protected internal virtual IOperator CreateChildCreater() {
354      CombinedOperator childCreater = new CombinedOperator();
355      childCreater.Name = "Create children";
356      SequentialProcessor seq = new SequentialProcessor();
357      OperatorExtractor selector = new OperatorExtractor();
358      selector.Name = "Selector (extr.)";
359      selector.GetVariableInfo("Operator").ActualName = "Selector";
360
361      SequentialSubScopesProcessor seqScopesProc = new SequentialSubScopesProcessor();
362      EmptyOperator emptyOpt = new EmptyOperator();
363      SequentialProcessor selectedProc = new SequentialProcessor();
364      ChildrenInitializer childInitializer = new ChildrenInitializer();
365      ((IntData)childInitializer.GetVariable("ParentsPerChild").Value).Data = 2;
366
367      OperatorExtractor crossover = new OperatorExtractor();
368      crossover.Name = "Crossover (extr.)";
369      crossover.GetVariableInfo("Operator").ActualName = "Crossover";
370      UniformSequentialSubScopesProcessor individualProc = new UniformSequentialSubScopesProcessor();
371      SequentialProcessor individualSeqProc = new SequentialProcessor();
372      StochasticBranch cond = new StochasticBranch();
373      cond.GetVariableInfo("Probability").ActualName = "MutationRate";
374      OperatorExtractor manipulator = new OperatorExtractor();
375      manipulator.Name = "Manipulator (extr.)";
376      manipulator.GetVariableInfo("Operator").ActualName = "Manipulator";
377      OperatorExtractor evaluator = new OperatorExtractor();
378      evaluator.Name = "Evaluator (extr.)";
379      evaluator.GetVariableInfo("Operator").ActualName = "Evaluator";
380      MeanSquaredErrorEvaluator validationEvaluator = new MeanSquaredErrorEvaluator();
381      validationEvaluator.GetVariableInfo("MSE").ActualName = "ValidationQuality";
382      validationEvaluator.GetVariableInfo("SamplesStart").ActualName = "ValidationSamplesStart";
383      validationEvaluator.GetVariableInfo("SamplesEnd").ActualName = "ValidationSamplesEnd";
384      Counter evalCounter = new Counter();
385      evalCounter.GetVariableInfo("Value").ActualName = "EvaluatedSolutions";
386      SubScopesRemover parentRefRemover = new SubScopesRemover();
387
388      Sorter sorter = new Sorter();
389      sorter.GetVariableInfo("Descending").ActualName = "Maximization";
390      sorter.GetVariableInfo("Value").ActualName = "Quality";
391
392
393      seq.AddSubOperator(selector);
394      seq.AddSubOperator(seqScopesProc);
395      seqScopesProc.AddSubOperator(emptyOpt);
396      seqScopesProc.AddSubOperator(selectedProc);
397      selectedProc.AddSubOperator(childInitializer);
398      selectedProc.AddSubOperator(individualProc);
399      individualProc.AddSubOperator(individualSeqProc);
400      individualSeqProc.AddSubOperator(crossover);
401      individualSeqProc.AddSubOperator(cond);
402      cond.AddSubOperator(manipulator);
403      individualSeqProc.AddSubOperator(evaluator);
404      individualSeqProc.AddSubOperator(validationEvaluator);
405      individualSeqProc.AddSubOperator(evalCounter);
406      individualSeqProc.AddSubOperator(parentRefRemover);
407      selectedProc.AddSubOperator(sorter);
408
409      childCreater.OperatorGraph.AddOperator(seq);
410      childCreater.OperatorGraph.InitialOperator = seq;
411      return childCreater;
412    }
413
414    protected internal virtual Model CreateGPModel(IScope bestModelScope) {
415      Engine.GlobalScope.AddSubScope(bestModelScope);
416      Model model = new Model();
417      Dataset ds = bestModelScope.GetVariableValue<Dataset>("Dataset", true);
418      model.Data = bestModelScope.GetVariableValue<IGeneticProgrammingModel>("FunctionTree", false);
419      model.Dataset = ds;
420      model.TargetVariable = ds.GetVariableName(bestModelScope.GetVariableValue<IntData>("TargetVariable", true).Data);
421      model.TrainingMeanSquaredError = bestModelScope.GetVariableValue<DoubleData>("Quality", false).Data;
422      model.ValidationMeanSquaredError = bestModelScope.GetVariableValue<DoubleData>("ValidationQuality", false).Data;
423      // calculate and set variable impacts
424      VariableEvaluationImpactCalculator evaluationImpactCalculator = new VariableEvaluationImpactCalculator();
425      VariableQualityImpactCalculator qualityImpactCalculator = new VariableQualityImpactCalculator();
426
427      evaluationImpactCalculator.Apply(bestModelScope);
428      qualityImpactCalculator.Apply(bestModelScope);
429
430      ItemList evaluationImpacts = bestModelScope.GetVariableValue<ItemList>("VariableEvaluationImpacts", false);
431      ItemList qualityImpacts = bestModelScope.GetVariableValue<ItemList>("VariableQualityImpacts", false);
432      foreach (ItemList row in evaluationImpacts) {
433        string variableName = ((StringData)row[0]).Data;
434        double impact = ((DoubleData)row[1]).Data;
435        model.SetVariableEvaluationImpact(variableName, impact);
436      }
437      foreach (ItemList row in qualityImpacts) {
438        string variableName = ((StringData)row[0]).Data;
439        double impact = ((DoubleData)row[1]).Data;
440        model.SetVariableQualityImpact(variableName, impact);
441      }
442      Engine.GlobalScope.RemoveSubScope(bestModelScope);
443      return model;
444    }
445
446    public override object Clone(IDictionary<Guid, object> clonedObjects) {
447      AlgorithmBase clone = (AlgorithmBase)base.Clone(clonedObjects);
448      clonedObjects.Add(Guid, clone);
449      clone.engine = (SequentialEngine.SequentialEngine)Auxiliary.Clone(Engine, clonedObjects);
450      return clone;
451    }
452
453    protected VariableInjector GetVariableInjector() {
454      CombinedOperator co1 = (CombinedOperator)Engine.OperatorGraph.InitialOperator;
455      // SequentialProcessor in GP
456      algorithm = (SequentialProcessor)co1.OperatorGraph.InitialOperator;
457      return (VariableInjector)algorithm.SubOperators[2];
458    }
459
460    protected RandomInjector GetRandomInjector() {
461      CombinedOperator co1 = (CombinedOperator)Engine.OperatorGraph.InitialOperator;
462      // SequentialProcessor in GP
463      algorithm = (SequentialProcessor)co1.OperatorGraph.InitialOperator;
464      return (RandomInjector)algorithm.SubOperators[0];
465    }
466
467    #region Persistence Methods
468    public override XmlNode GetXmlNode(string name, XmlDocument document, IDictionary<Guid, IStorable> persistedObjects) {
469      XmlNode node = base.GetXmlNode(name, document, persistedObjects);
470      node.AppendChild(PersistenceManager.Persist("Engine", Engine, document, persistedObjects));
471      return node;
472    }
473    public override void Populate(XmlNode node, IDictionary<Guid, IStorable> restoredObjects) {
474      base.Populate(node, restoredObjects);
475      engine = (SequentialEngine.SequentialEngine)PersistenceManager.Restore(node.SelectSingleNode("Engine"), restoredObjects);
476    }
477    #endregion
478
479
480  }
481}
Note: See TracBrowser for help on using the repository browser.