[18063] | 1 | using System;
|
---|
[18146] | 2 | using System.Collections.Generic;
|
---|
[18068] | 3 | using System.Linq;
|
---|
[18063] | 4 | using HEAL.Attic;
|
---|
| 5 | using HeuristicLab.Common;
|
---|
[18146] | 6 | using HeuristicLab.Core;
|
---|
[18063] | 7 | using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
|
---|
| 8 |
|
---|
| 9 | namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
|
---|
| 10 | [StorableType("E3C038DB-C6AA-457D-9F65-AF16C44CCE22")]
|
---|
| 11 | [Item("StructureTemplate", "Structure Template")]
|
---|
| 12 | public class StructureTemplate : Item {
|
---|
| 13 |
|
---|
[18065] | 14 | #region Properties
|
---|
[18063] | 15 | [Storable]
|
---|
[18075] | 16 | private string template;
|
---|
[18063] | 17 | public string Template {
|
---|
[18146] | 18 | get => template;
|
---|
[18063] | 19 | set {
|
---|
[18187] | 20 | if (value == template) return;
|
---|
| 21 |
|
---|
[18068] | 22 | template = value;
|
---|
[18187] | 23 | var parsedTree = Parser.Parse(template);
|
---|
| 24 | if (applyLinearScaling)
|
---|
| 25 | parsedTree = AddLinearScalingTerms(parsedTree);
|
---|
| 26 | Tree = parsedTree;
|
---|
[18068] | 27 | OnChanged();
|
---|
[18146] | 28 | }
|
---|
[18063] | 29 | }
|
---|
| 30 |
|
---|
[18188] | 31 | private ISymbolicExpressionTree _oldTree;
|
---|
| 32 | [Storable(OldName = "treeWithoutLinearScaling")]
|
---|
| 33 | private ISymbolicExpressionTree _OldTreeW {
|
---|
| 34 | set => _oldTree = value;
|
---|
| 35 | }
|
---|
| 36 |
|
---|
[18063] | 37 | [Storable]
|
---|
[18187] | 38 | private ISymbolicExpressionTree tree;
|
---|
[18069] | 39 | public ISymbolicExpressionTree Tree {
|
---|
[18187] | 40 | get => tree;
|
---|
[18069] | 41 | private set {
|
---|
[18187] | 42 | tree = value;
|
---|
[18164] | 43 |
|
---|
| 44 | var newFunctions = GetSubFunctions();
|
---|
[18187] | 45 | var oldFunctions = subFunctions?.Intersect(newFunctions)
|
---|
[18164] | 46 | ?? Enumerable.Empty<SubFunction>();
|
---|
| 47 | // adds new functions and keeps the old ones (if they match)
|
---|
| 48 | subFunctions = newFunctions.Except(oldFunctions).Concat(oldFunctions).ToList();
|
---|
[18069] | 49 | }
|
---|
| 50 | }
|
---|
[18063] | 51 |
|
---|
| 52 | [Storable]
|
---|
[18151] | 53 | private IList<SubFunction> subFunctions = new List<SubFunction>();
|
---|
| 54 | public IEnumerable<SubFunction> SubFunctions => subFunctions;
|
---|
[18063] | 55 |
|
---|
[18069] | 56 | [Storable]
|
---|
[18075] | 57 | private bool applyLinearScaling;
|
---|
[18069] | 58 | public bool ApplyLinearScaling {
|
---|
| 59 | get => applyLinearScaling;
|
---|
| 60 | set {
|
---|
[18187] | 61 | if (value == applyLinearScaling) return;
|
---|
| 62 |
|
---|
[18069] | 63 | applyLinearScaling = value;
|
---|
[18187] | 64 | if (applyLinearScaling) Tree = AddLinearScalingTerms(Tree);
|
---|
| 65 | else Tree = RemoveLinearScalingTerms(Tree);
|
---|
| 66 |
|
---|
[18069] | 67 | OnChanged();
|
---|
| 68 | }
|
---|
| 69 | }
|
---|
| 70 |
|
---|
[18063] | 71 | protected InfixExpressionParser Parser { get; set; } = new InfixExpressionParser();
|
---|
[18068] | 72 |
|
---|
[18065] | 73 | #endregion
|
---|
[18063] | 74 |
|
---|
[18065] | 75 | #region Events
|
---|
| 76 | public event EventHandler Changed;
|
---|
[18146] | 77 |
|
---|
[18065] | 78 | private void OnChanged() => Changed?.Invoke(this, EventArgs.Empty);
|
---|
| 79 | #endregion
|
---|
[18063] | 80 |
|
---|
| 81 | #region Constructors
|
---|
[18075] | 82 | public StructureTemplate() {
|
---|
| 83 | Reset();
|
---|
| 84 | }
|
---|
[18063] | 85 |
|
---|
| 86 | [StorableConstructor]
|
---|
| 87 | protected StructureTemplate(StorableConstructorFlag _) : base(_) { }
|
---|
| 88 |
|
---|
[18074] | 89 | protected StructureTemplate(StructureTemplate original, Cloner cloner) : base(original, cloner) {
|
---|
[18187] | 90 | this.tree = cloner.Clone(original.tree);
|
---|
| 91 | this.template = original.Template;
|
---|
| 92 | this.applyLinearScaling = original.ApplyLinearScaling;
|
---|
[18151] | 93 | this.subFunctions = original.subFunctions.Select(cloner.Clone).ToList();
|
---|
| 94 | RegisterEventHandlers();
|
---|
[18074] | 95 | }
|
---|
[18151] | 96 |
|
---|
| 97 |
|
---|
| 98 | [StorableHook(HookType.AfterDeserialization)]
|
---|
| 99 | private void AfterDeserialization() {
|
---|
[18188] | 100 | if (Tree == null && _oldTree != null) {
|
---|
| 101 | if (ApplyLinearScaling) _oldTree = AddLinearScalingTerms(_oldTree);
|
---|
| 102 | Tree = _oldTree;
|
---|
| 103 | _oldTree = null;
|
---|
| 104 | }
|
---|
| 105 |
|
---|
[18151] | 106 | RegisterEventHandlers();
|
---|
| 107 | }
|
---|
[18063] | 108 | #endregion
|
---|
| 109 |
|
---|
| 110 | #region Cloning
|
---|
| 111 | public override IDeepCloneable Clone(Cloner cloner) =>
|
---|
| 112 | new StructureTemplate(this, cloner);
|
---|
| 113 | #endregion
|
---|
| 114 |
|
---|
[18075] | 115 | public void Reset() {
|
---|
[18151] | 116 | subFunctions = new List<SubFunction>();
|
---|
[18187] | 117 | tree = null;
|
---|
[18152] | 118 | Template = "f(_)";
|
---|
[18075] | 119 | }
|
---|
[18072] | 120 |
|
---|
[18151] | 121 | private IList<SubFunction> GetSubFunctions() {
|
---|
| 122 | var subFunctions = new List<SubFunction>();
|
---|
[18069] | 123 | foreach (var node in Tree.IterateNodesPrefix())
|
---|
[18068] | 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 |
|
---|
[18151] | 128 | var existingSubFunction = subFunctions.Where(x => x.Name == subFunctionTreeNode.Name).FirstOrDefault();
|
---|
| 129 | if (existingSubFunction != null) {
|
---|
[18187] | 130 | // an existing subFunction must have the same signature
|
---|
| 131 | if (!existingSubFunction.Arguments.SequenceEqual(subFunctionTreeNode.Arguments))
|
---|
[18068] | 132 | throw new ArgumentException(
|
---|
[18151] | 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)})).");
|
---|
[18068] | 136 | } else {
|
---|
| 137 | var subFunction = new SubFunction() {
|
---|
| 138 | Name = subFunctionTreeNode.Name,
|
---|
| 139 | Arguments = subFunctionTreeNode.Arguments
|
---|
| 140 | };
|
---|
| 141 | subFunction.Changed += OnSubFunctionChanged;
|
---|
[18151] | 142 | subFunctions.Add(subFunction);
|
---|
[18068] | 143 | }
|
---|
[18065] | 144 | }
|
---|
[18069] | 145 | return subFunctions;
|
---|
[18063] | 146 | }
|
---|
[18068] | 147 |
|
---|
[18151] | 148 | private void RegisterEventHandlers() {
|
---|
[18187] | 149 | foreach (var sf in SubFunctions) {
|
---|
[18151] | 150 | sf.Changed += OnSubFunctionChanged;
|
---|
| 151 | }
|
---|
| 152 | }
|
---|
| 153 |
|
---|
[18187] | 154 | private static ISymbolicExpressionTree AddLinearScalingTerms(ISymbolicExpressionTree tree) {
|
---|
[18069] | 155 | var clonedTree = (ISymbolicExpressionTree)tree.Clone();
|
---|
| 156 | var startNode = clonedTree.Root.Subtrees.First();
|
---|
| 157 | var template = startNode.Subtrees.First();
|
---|
| 158 |
|
---|
[18187] | 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);
|
---|
[18069] | 163 |
|
---|
[18146] | 164 | addNode.AddSubtree(offsetNode);
|
---|
[18069] | 165 | addNode.AddSubtree(mulNode);
|
---|
[18146] | 166 | mulNode.AddSubtree(scaleNode);
|
---|
| 167 |
|
---|
[18069] | 168 | startNode.RemoveSubtree(0);
|
---|
| 169 | startNode.AddSubtree(addNode);
|
---|
| 170 | mulNode.AddSubtree(template);
|
---|
| 171 | return clonedTree;
|
---|
| 172 | }
|
---|
| 173 |
|
---|
[18187] | 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 |
|
---|
[18068] | 198 | private void OnSubFunctionChanged(object sender, EventArgs e) => OnChanged();
|
---|
[18063] | 199 | }
|
---|
| 200 | }
|
---|