Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3136_Structural_GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/StructureTemplate/StructureTemplate.cs @ 18187

Last change on this file since 18187 was 18187, checked in by mkommend, 2 years ago

#3136: Refactored saved trees in structure template.

File size: 6.5 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using HEAL.Attic;
5using HeuristicLab.Common;
6using HeuristicLab.Core;
7using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
8
9namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
10  [StorableType("E3C038DB-C6AA-457D-9F65-AF16C44CCE22")]
11  [Item("StructureTemplate", "Structure Template")]
12  public class StructureTemplate : Item {
13
14    #region Properties
15    [Storable]
16    private string template;
17    public string Template {
18      get => template;
19      set {
20        if (value == template) return;
21
22        template = value;
23        var parsedTree = Parser.Parse(template);
24        if (applyLinearScaling)
25          parsedTree = AddLinearScalingTerms(parsedTree);
26        Tree = parsedTree;
27        OnChanged();
28      }
29    }
30
31    [Storable]
32    private ISymbolicExpressionTree tree;
33    public ISymbolicExpressionTree Tree {
34      get => tree;
35      private set {
36        tree = value;
37
38        var newFunctions = GetSubFunctions();
39        var oldFunctions = subFunctions?.Intersect(newFunctions)
40                           ?? Enumerable.Empty<SubFunction>();
41        // adds new functions and keeps the old ones (if they match)
42        subFunctions = newFunctions.Except(oldFunctions).Concat(oldFunctions).ToList();
43      }
44    }
45
46    [Storable]
47    private IList<SubFunction> subFunctions = new List<SubFunction>();
48    public IEnumerable<SubFunction> SubFunctions => subFunctions;
49
50    [Storable]
51    private bool applyLinearScaling;
52    public bool ApplyLinearScaling {
53      get => applyLinearScaling;
54      set {
55        if (value == applyLinearScaling) return;
56
57        applyLinearScaling = value;
58        if (applyLinearScaling) Tree = AddLinearScalingTerms(Tree);
59        else Tree = RemoveLinearScalingTerms(Tree);
60
61        OnChanged();
62      }
63    }
64
65    protected InfixExpressionParser Parser { get; set; } = new InfixExpressionParser();
66
67    #endregion
68
69    #region Events
70    public event EventHandler Changed;
71
72    private void OnChanged() => Changed?.Invoke(this, EventArgs.Empty);
73    #endregion
74
75    #region Constructors
76    public StructureTemplate() {
77      Reset();
78    }
79
80    [StorableConstructor]
81    protected StructureTemplate(StorableConstructorFlag _) : base(_) { }
82
83    protected StructureTemplate(StructureTemplate original, Cloner cloner) : base(original, cloner) {
84      this.tree = cloner.Clone(original.tree);
85      this.template = original.Template;
86      this.applyLinearScaling = original.ApplyLinearScaling;
87      this.subFunctions = original.subFunctions.Select(cloner.Clone).ToList();
88      RegisterEventHandlers();
89    }
90
91
92    [StorableHook(HookType.AfterDeserialization)]
93    private void AfterDeserialization() {
94      RegisterEventHandlers();
95    }
96    #endregion
97
98    #region Cloning
99    public override IDeepCloneable Clone(Cloner cloner) =>
100      new StructureTemplate(this, cloner);
101    #endregion
102
103    public void Reset() {
104      subFunctions = new List<SubFunction>();
105      tree = null;
106      Template = "f(_)";
107    }
108
109    private IList<SubFunction> GetSubFunctions() {
110      var subFunctions = new List<SubFunction>();
111      foreach (var node in Tree.IterateNodesPrefix())
112        if (node is SubFunctionTreeNode subFunctionTreeNode) {
113          if (!subFunctionTreeNode.Arguments.Any())
114            throw new ArgumentException($"The sub-function '{subFunctionTreeNode}' requires inputs (e.g. {subFunctionTreeNode.Name}(var1, var2)).");
115
116          var existingSubFunction = subFunctions.Where(x => x.Name == subFunctionTreeNode.Name).FirstOrDefault();
117          if (existingSubFunction != null) {
118            // an existing subFunction must have the same signature
119            if (!existingSubFunction.Arguments.SequenceEqual(subFunctionTreeNode.Arguments))
120              throw new ArgumentException(
121                $"The sub-function '{existingSubFunction.Name}' has (at least two) different signatures " +
122                $"({existingSubFunction.Name}({string.Join(",", existingSubFunction.Arguments)}) <> " +
123                $"{subFunctionTreeNode.Name}({string.Join(",", subFunctionTreeNode.Arguments)})).");
124          } else {
125            var subFunction = new SubFunction() {
126              Name = subFunctionTreeNode.Name,
127              Arguments = subFunctionTreeNode.Arguments
128            };
129            subFunction.Changed += OnSubFunctionChanged;
130            subFunctions.Add(subFunction);
131          }
132        }
133      return subFunctions;
134    }
135
136    private void RegisterEventHandlers() {
137      foreach (var sf in SubFunctions) {
138        sf.Changed += OnSubFunctionChanged;
139      }
140    }
141
142    private static ISymbolicExpressionTree AddLinearScalingTerms(ISymbolicExpressionTree tree) {
143      var clonedTree = (ISymbolicExpressionTree)tree.Clone();
144      var startNode = clonedTree.Root.Subtrees.First();
145      var template = startNode.Subtrees.First();
146
147      var addNode = new Addition().CreateTreeNode();
148      var mulNode = new Multiplication().CreateTreeNode();
149      var offsetNode = new NumberTreeNode(0.0);
150      var scaleNode = new NumberTreeNode(1.0);
151
152      addNode.AddSubtree(offsetNode);
153      addNode.AddSubtree(mulNode);
154      mulNode.AddSubtree(scaleNode);
155
156      startNode.RemoveSubtree(0);
157      startNode.AddSubtree(addNode);
158      mulNode.AddSubtree(template);
159      return clonedTree;
160    }
161
162    private static ISymbolicExpressionTree RemoveLinearScalingTerms(ISymbolicExpressionTree tree) {
163      var clonedTree = (ISymbolicExpressionTree)tree.Clone();
164      var startNode = clonedTree.Root.Subtrees.First();
165
166      //check for scaling terms
167      var addNode = startNode.GetSubtree(0);
168      var offsetNode = addNode.GetSubtree(0);
169      var mulNode = addNode.GetSubtree(1);
170      var scaleNode = mulNode.GetSubtree(0);
171      var templateNode = mulNode.GetSubtree(1);
172
173      var error = false;
174      if (addNode.Symbol is not Addition) error = true;
175      if (mulNode.Symbol is not Multiplication) error = true;
176      if (offsetNode is not NumberTreeNode offset || offset.Value != 0.0) error = true;
177      if (scaleNode is not NumberTreeNode scale || scale.Value != 1.0) error = true;
178      if (error) throw new ArgumentException("Scaling terms cannot be found.");
179
180      startNode.RemoveSubtree(0);
181      startNode.AddSubtree(templateNode);
182
183      return clonedTree;
184    }
185
186    private void OnSubFunctionChanged(object sender, EventArgs e) => OnChanged();
187  }
188}
Note: See TracBrowser for help on using the repository browser.