Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/AlgebraicInterval.cs @ 17869

Last change on this file since 17869 was 17314, checked in by gkronber, 5 years ago

#2994: fix bug in calculation of cos() and sin()

File size: 11.1 KB
Line 
1using System;
2using System.Diagnostics;
3using System.Linq;
4
5namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
6  // this is our own implementation of interval arithmetic
7  // for a well worked out definition of interval operations for IEEE reals see:
8  // Stahl: Interval Methods for Bounding the Range of Polynomials and Solving Systems of Nonlinear Equations, Dissertation, JKU, 1995
9  [DebuggerDisplay("[{low.Value}..{high.Value}]")]
10  public class AlgebraicInterval : IAlgebraicType<AlgebraicInterval> {
11
12    // turn a double into an interval (of size 0)
13    public static implicit operator AlgebraicInterval(double value) { return new AlgebraicInterval(value, value); }
14
15    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
16    private MultivariateDual<AlgebraicDouble> low;
17    public MultivariateDual<AlgebraicDouble> LowerBound => low.Clone();
18
19    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
20    private MultivariateDual<AlgebraicDouble> high;
21    public MultivariateDual<AlgebraicDouble> UpperBound => high.Clone();
22
23
24    public AlgebraicInterval() : this(double.NegativeInfinity, double.PositiveInfinity) { }
25
26    public AlgebraicInterval(MultivariateDual<AlgebraicDouble> low, MultivariateDual<AlgebraicDouble> high) {
27      this.low = low.Clone();
28      this.high = high.Clone();
29    }
30
31    public AlgebraicInterval(double low, double high) {
32      this.low = new MultivariateDual<AlgebraicDouble>(new AlgebraicDouble(low));
33      this.high = new MultivariateDual<AlgebraicDouble>(new AlgebraicDouble(high));
34    }
35
36    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
37    public AlgebraicInterval Zero => new AlgebraicInterval(0.0, 0.0);
38    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
39    public AlgebraicInterval One => new AlgebraicInterval(1.0, 1.0);
40
41    public AlgebraicInterval Add(AlgebraicInterval a) {
42      low.Add(a.low);
43      high.Add(a.high);
44      return this;
45    }
46
47    public AlgebraicInterval Mul(AlgebraicInterval a) {
48      var v1 = low.Clone().Mul(a.low);
49      var v2 = low.Clone().Mul(a.high);
50      var v3 = high.Clone().Mul(a.low);
51      var v4 = high.Clone().Mul(a.high);
52
53      AssignLowAndHigh(v1, v2, v3, v4);
54
55      return this;
56    }
57
58    public AlgebraicInterval Assign(AlgebraicInterval a) {
59      low = a.low;
60      high = a.high;
61      return this;
62    }
63
64    public AlgebraicInterval AssignCos(AlgebraicInterval a) {
65      return AssignSin(a.Clone().Add(new AlgebraicInterval(Math.PI / 2, Math.PI / 2)));
66    }
67
68    public AlgebraicInterval Div(AlgebraicInterval a) {
69      if (a.Contains(0.0)) {
70        if (a.low.Value.Value == 0 && a.high.Value.Value == 0) {
71          if (this.low.Value >= 0) {
72            // pos / 0
73          } else if (this.high.Value <= 0) {
74            // neg / 0
75          } else {
76            low = new MultivariateDual<AlgebraicDouble>(double.NegativeInfinity);
77            high = new MultivariateDual<AlgebraicDouble>(double.PositiveInfinity);
78          }
79        } else if (a.low.Value.Value >= 0) {
80          // a is positive
81          Mul(new AlgebraicInterval(a.Clone().high.Inv(), new MultivariateDual<AlgebraicDouble>(double.PositiveInfinity)));
82        } else if (a.high.Value <= 0) {
83          // a is negative
84          Mul(new AlgebraicInterval(new MultivariateDual<AlgebraicDouble>(double.NegativeInfinity), a.low.Clone().Inv()));
85        } else {
86          // a is interval over zero
87          low = new MultivariateDual<AlgebraicDouble>(double.NegativeInfinity);
88          high = new MultivariateDual<AlgebraicDouble>(double.PositiveInfinity);
89        }
90      } else {
91        Mul(new AlgebraicInterval(a.high.Clone().Inv(), a.low.Clone().Inv())); // inverting leads to inverse roles of high and low
92      }
93      return this;
94    }
95
96    public AlgebraicInterval AssignExp(AlgebraicInterval a) {
97      low.AssignExp(a.low);
98      high.AssignExp(a.high);
99      return this;
100    }
101
102    // tanh is a bijective function
103    public AlgebraicInterval AssignTanh(AlgebraicInterval a) {
104      low.AssignTanh(a.low);
105      high.AssignTanh(a.high);
106      return this;
107    }
108
109    public AlgebraicInterval AssignIntPower(AlgebraicInterval a, int p) {
110      if (p < 0) {  // x^-3 == 1/(x^3)
111        AssignIntPower(a, -p);
112        return AssignInv(this);
113      } else if (p == 0) {
114        if (a.Contains(0.0)) {
115          // => 0^0 = 0 ; might not be relevant
116          low = new MultivariateDual<AlgebraicDouble>(0.0);
117          high = new MultivariateDual<AlgebraicDouble>(1.0);
118          return this;
119        } else {
120          // => 1
121          low = new MultivariateDual<AlgebraicDouble>(1.0);
122          high = new MultivariateDual<AlgebraicDouble>(1.0);
123          return this;
124        }
125      } else if (p == 1) return this;
126      else if (p % 2 == 0) {
127        // p is even => interval must be positive
128        if (a.Contains(0.0)) {
129          low = new MultivariateDual<AlgebraicDouble>(0.0);
130          AssignMax(high, a.low.IntPower(p), a.high.IntPower(p));
131        } else {
132          AssignLowAndHigh(a.low.IntPower(p), a.high.IntPower(p));
133        }
134      } else {
135        // p is uneven
136        if (a.Contains(0.0)) {
137          low.AssignIntPower(a.low, p);
138          high.AssignIntPower(a.high, p);
139        } else {
140          var lowPower = a.low.IntPower(p);
141          var highPower = a.high.IntPower(p);
142          AssignMin(low, lowPower, highPower);
143          AssignMax(high, lowPower, highPower);
144        }
145      }
146      return this;
147    }
148
149    public AlgebraicInterval AssignIntRoot(AlgebraicInterval a, int r) {
150      if (r == 0) { low = new MultivariateDual<AlgebraicDouble>(double.NaN); high = new MultivariateDual<AlgebraicDouble>(double.NaN); return this; }
151      if (r == 1) return this;
152      if (r < 0) {
153        // x^ (-1/2) = 1 / (x^(1/2))
154        AssignIntRoot(a, -r);
155        return AssignInv(this);
156      } else {
157        // root only defined for positive arguments for even roots
158        if (r % 2 == 0 && a.LowerBound.Value.Value < 0) {
159          low = new MultivariateDual<AlgebraicDouble>(double.NaN);
160          high = new MultivariateDual<AlgebraicDouble>(double.NaN);
161          return this;
162        } else {
163          low.AssignIntRoot(a.low, r);
164          high.AssignIntRoot(a.high, r);
165          return this;
166        }
167      }
168    }
169
170    public AlgebraicInterval AssignInv(AlgebraicInterval a) {
171      low = new MultivariateDual<AlgebraicDouble>(1.0);
172      high = new MultivariateDual<AlgebraicDouble>(1.0);
173      return Div(a);
174    }
175
176    public AlgebraicInterval AssignLog(AlgebraicInterval a) {
177      low.AssignLog(a.low);
178      high.AssignLog(a.high);
179      return this;
180    }
181
182    public AlgebraicInterval AssignNeg(AlgebraicInterval a) {
183      low.AssignNeg(a.high);
184      high.AssignNeg(a.low);
185      return this;
186    }
187
188    public AlgebraicInterval Scale(double s) {
189      low.Scale(s);
190      high.Scale(s);
191      if (s < 0) {
192        var t = low;
193        low = high;
194        high = t;
195      }
196      return this;
197    }
198
199
200    // TODO: move to constants
201    private const double pi_2 = Math.PI / 2.0;
202    private static readonly double[] maxima = new double[] { -3 * pi_2, pi_2, 5 * pi_2 };
203    private static readonly double[] minima = new double[] { -5 * pi_2, -pi_2, 3 * pi_2 };
204
205    public AlgebraicInterval AssignSin(AlgebraicInterval a) {
206      var lower = a.LowerBound.Value.Value;
207      var size = a.UpperBound.Value.Value - lower;
208      if (size < 0) throw new InvalidProgramException(); // ASSERT interval >= 0;
209
210      if (size >= Math.PI * 2) {
211        low = new MultivariateDual<AlgebraicDouble>(-1.0); // zero gradient
212        high = new MultivariateDual<AlgebraicDouble>(1.0);
213        return this;
214      }
215
216      // assume low and high are in the same quadrant
217      AssignLowAndHigh(a.LowerBound.Clone().Sin(), a.UpperBound.Clone().Sin()); // AssignLowAndHigh determines the lower and higher value
218
219      // override min and max if necessary
220
221      // shift interval 'a' into the range [-2pi .. 2pi] without changing the size of the interval to simplify the checks
222      lower = lower % (2 * Math.PI); // lower in [-2pi .. 2pi]     
223
224      // handle min = -1 and max = 1 cases explicitly
225
226      // override min and max if necessary
227      if (maxima.Any(m => lower < m && lower + size > m)) {
228        // max = 1
229        high = new MultivariateDual<AlgebraicDouble>(1.0); // zero gradient
230      }
231
232      if (minima.Any(m => lower < m && lower + size > m)) {
233        // min = -1;
234        low = new MultivariateDual<AlgebraicDouble>(-1.0); // zero gradient
235      }
236      return this;
237    }
238
239    public AlgebraicInterval Sub(AlgebraicInterval a) {
240      // [x1,x2] − [y1,y2] = [x1 − y2,x2 − y1]
241      low.Sub(a.high);
242      high.Sub(a.low);
243      return this;
244    }
245
246    public AlgebraicInterval Clone() {
247      return new AlgebraicInterval(low, high);
248    }
249
250    public bool Contains(double val) {
251      return LowerBound.Value.Value <= val && val <= UpperBound.Value.Value;
252    }
253
254    public AlgebraicInterval AssignAbs(AlgebraicInterval a) {
255      if (a.Contains(0.0)) {
256        low.Assign(new MultivariateDual<AlgebraicDouble>(0.0)); // lost gradient for lower bound
257        AssignMax(high, a.low.Clone().Abs(), a.high.Clone().Abs());
258      } else {
259        var abslow = a.low.Clone().Abs();
260        var abshigh = a.high.Clone().Abs();
261        AssignLowAndHigh(abslow, abshigh);
262      }
263      return this;
264    }
265
266    public AlgebraicInterval AssignSgn(AlgebraicInterval a) {
267      low.AssignSgn(a.low);
268      high.AssignSgn(a.high);
269      return this;
270    }
271
272
273    #region helper
274    private void AssignMin(MultivariateDual<AlgebraicDouble> dest, MultivariateDual<AlgebraicDouble> a, MultivariateDual<AlgebraicDouble> b) {
275      if (a.Value.CompareTo(b.Value) <= 0) {
276        dest.Assign(a);
277      } else {
278        dest.Assign(b);
279      }
280    }
281    private void AssignMax(MultivariateDual<AlgebraicDouble> dest, MultivariateDual<AlgebraicDouble> a, MultivariateDual<AlgebraicDouble> b) {
282      if (a.Value.CompareTo(b.Value) <= 0) {
283        dest.Assign(b);
284      } else {
285        dest.Assign(a);
286      }
287    }
288
289    // determines the smaller and larger value and sets low and high accordingly
290    private void AssignLowAndHigh(MultivariateDual<AlgebraicDouble> a, MultivariateDual<AlgebraicDouble> b) {
291      // we must make sure that low and high are different objects when a == b
292      if (a.Value.CompareTo(b.Value) == 0) {
293        low.Assign(a);
294        high.Assign(b);
295      } else {
296        AssignMin(low, a, b);
297        AssignMax(high, a, b);
298      }
299    }
300    private void AssignLowAndHigh(params MultivariateDual<AlgebraicDouble>[] xs) {
301      if (xs.Length <= 2) throw new ArgumentException("need at least 3 arguments");
302      AssignLowAndHigh(xs[0], xs[1]);
303      for (int i = 2; i < xs.Length; i++) {
304        // we must make sure that low and high are different objects when a == b
305        if (low.Value.CompareTo(xs[i].Value) > 0) low.Assign(xs[i]);
306        if (high.Value.CompareTo(xs[i].Value) < 0) high.Assign(xs[i]);
307      }
308    }
309    #endregion
310
311  }
312}
Note: See TracBrowser for help on using the repository browser.