Free cookie consent management tool by TermsFeed Policy Generator

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

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

#3136: Fixed backwards compatibility of StructureTemplate

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