source: branches/3022-FastFunctionExtraction/FFX/SimpleBasisFunction.cs @ 17779

Last change on this file since 17779 was 17779, checked in by gkronber, 12 months ago

#3022: made a few changes while reviewing the code.

File size: 4.6 KB
Line 
1using HeuristicLab.Collections;
2using HeuristicLab.Common;
3using HeuristicLab.Problems.DataAnalysis;
4using System;
5using System.Globalization;
6using System.Linq;
7
8namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
9
10    internal struct SimpleBasisFunction : ISimpleBasisFunction {
11        public double Exponent { get; set; }
12        public NonlinearOperator Operator { get; set; }
13        public bool IsDenominator { get; set; }
14        public string Feature { get; set; }
15        public double Threshold { get; set; } // only relevant for hinge function
16        public bool HasExponent => !(Exponent == 1.0);
17        public bool HasOperator => Operator != NonlinearOperator.None;
18        public int Complexity => Operator == NonlinearOperator.GT_Hinge || Operator == NonlinearOperator.LT_Hinge ? 3 : 1;
19
20        public SimpleBasisFunction(string feature, double exponent = 1, NonlinearOperator op = NonlinearOperator.None, bool isNominator = true, double threshold = 0) {
21            Feature = feature ?? throw new ArgumentNullException(nameof(feature));
22            Exponent = exponent;
23            Operator = op;
24            IsDenominator = isNominator;
25            Threshold = threshold;
26        }
27
28        public IBasisFunction DeepCopy() {
29            return new SimpleBasisFunction(Feature, Exponent, Operator, IsDenominator, Threshold);
30        }
31
32        public override string ToString() {
33            var culture = new CultureInfo("en-US");
34            string str = $"\"{Feature}\"";
35
36            if (HasExponent) {
37                if (Exponent.IsAlmost((double)-1 / 2)) str = $"1 / SQRT({str})";
38                else if (Exponent.IsAlmost((double)1 / 2)) str = $"SQRT({str})";
39                else if (Exponent.IsAlmost(1)) str = $"1 / {str}";
40                else if (Exponent.IsAlmost(2)) str = $"SQR({str})";
41                else if (Exponent.IsAlmost(3)) str = $"CUBE({str})";
42                else str = $"({str}) ^ ({Exponent.ToString(culture)})";
43            }
44
45            if (!HasOperator) return str;
46
47            var thr = Threshold.ToString(culture);
48            if (Operator == NonlinearOperator.LT_Hinge) {
49                var expr = $"{str} {((Threshold >= 0) ? " - " + Math.Abs(Threshold).ToString(culture) : " + " + Math.Abs(Threshold).ToString(culture))}";
50                str = $"IF(GT(0, {expr}), 0, {expr})";
51            } else if (Operator == NonlinearOperator.GT_Hinge) {
52                var expr = $"{thr} - {str}";
53                str = $"IF(GT(0, {expr}), 0, {expr})";
54            } else {
55                str = $"{opToStr.GetByFirst(Operator)}({str})";
56            }
57            return str;
58        }
59
60        public double[] Evaluate(IRegressionProblemData data) {
61            var exp = Exponent;
62            // exponentVals : e.g. "x3^2"
63            var exponentVals = data.Dataset.GetDoubleValues(Feature)
64                .Select(val => Math.Pow(val, exp))
65                .ToArray();
66            // vals: e.g. "log(x3^2)
67            var vals = Eval(exponentVals);
68
69            if (!IsDenominator) {
70                var y = data.TargetVariableValues;
71                vals = vals.Zip(y, (a, b) => -a * b).ToArray();
72            }
73            return vals;
74        }
75
76        private double[] Eval(double[] x) {
77            var thr = this.Threshold;
78            switch (Operator) {
79                case NonlinearOperator.None:
80                    return x;
81                case NonlinearOperator.Abs:
82                    return x.Select(val => Math.Abs(val)).ToArray();
83                case NonlinearOperator.Log:
84                    return x.Select(val => Math.Log(val)).ToArray();
85                case NonlinearOperator.Sin:
86                    return x.Select(val => Math.Sin(val)).ToArray();
87                case NonlinearOperator.Cos:
88                    return x.Select(val => Math.Cos(val)).ToArray();
89                case NonlinearOperator.GT_Hinge:
90                    return x.Select(val => Math.Max(0, thr - val)).ToArray();
91                case NonlinearOperator.LT_Hinge:
92                    return x.Select(val => Math.Max(0, val - thr)).ToArray();
93                default:
94                    throw new Exception("Unimplemented operator: " + Operator.ToString());
95            }
96        }
97
98        private static readonly BidirectionalDictionary<NonlinearOperator, string> opToStr = new BidirectionalDictionary<NonlinearOperator, string>() {
99            { NonlinearOperator.Abs, "ABS"},
100            { NonlinearOperator.Log, "LOG"},
101            { NonlinearOperator.Sin, "SIN"},
102            { NonlinearOperator.Cos, "COS"}
103        };
104    }
105}
Note: See TracBrowser for help on using the repository browser.