1 | using HeuristicLab.Collections;
|
---|
2 | using HeuristicLab.Common;
|
---|
3 | using HeuristicLab.Problems.DataAnalysis;
|
---|
4 | using System;
|
---|
5 | using System.Globalization;
|
---|
6 | using System.Linq;
|
---|
7 |
|
---|
8 | namespace 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 | }
|
---|