Free cookie consent management tool by TermsFeed Policy Generator

Changeset 17350


Ignore:
Timestamp:
11/13/19 14:50:26 (4 years ago)
Author:
chaider
Message:

#3032

  • Added IsAlmost method with an epsilon parameter
  • Added check for floating point issues at interval constructor
  • Updated absolute operator (jump over zero between lower and upper bound)
  • Updated analytical quotient to use already implemented operators to build it
  • Updated test cases for operators
  • Refactored test method names
Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/HeuristicLab.Common/3.3/DoubleExtensions.cs

    r17180 r17350  
    2323namespace HeuristicLab.Common {
    2424  public static class DoubleExtensions {
     25    /// <summary>
     26    /// Compares the similarity of value x and value y with a precision of 1.0E-12.
     27    /// </summary>
     28    /// <param name="x">First double value to be checked</param>
     29    /// <param name="y">Second double value to compare with</param>
     30    /// <returns></returns>
    2531    public static bool IsAlmost(this double x, double y) {
     32      var epsilon = 1.0E-12;
     33      return IsAlmost(x, y, epsilon);
     34    }
     35
     36    /// <summary>
     37    /// Compares the similarity of value x and value y with a given precision (epsilon).
     38    /// </summary>
     39    /// <param name="x">First double value to be checked</param>
     40    /// <param name="y">Second double value to compare with</param>
     41    /// <param name="epsilon">Error term to specify the precision</param>
     42    /// <returns></returns>
     43    public static bool IsAlmost(this double x, double y, double epsilon) {
    2644      if (double.IsInfinity(x)) {
    2745        if (x > 0) return double.IsPositiveInfinity(y);
    2846        else return double.IsNegativeInfinity(y);
    2947      } else {
    30         return Math.Abs(x - y) < 1.0E-12;
     48        return Math.Abs(x - y) < epsilon;
    3149      }
    3250    }
  • trunk/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval.cs

    r17348 r17350  
    3737    protected Interval(StorableConstructorFlag _) { }
    3838
     39    /// <summary>
     40    /// Creates an interval with given bounds, where lower bound must be smaller than
     41    /// the upper bound. Floating point precision errors trough calculations are fixed by,
     42    /// checking if the intervals are almost the same (E-12). If this is the case, the bounds
     43    /// will be set to the bound closer to zero.
     44    /// </summary>
     45    /// <param name="lowerBound">Lower bound of the interval</param>
     46    /// <param name="upperBound">Upper bound of the interval</param>
    3947    public Interval(double lowerBound, double upperBound) {
     48      if (lowerBound.IsAlmost(upperBound)) {
     49        //If the bounds go over zero
     50        if (lowerBound <= 0 && upperBound >= 0) {
     51          lowerBound = 0.0;
     52          upperBound = 0.0;
     53          //Interval is negative
     54        } else if (upperBound < 0) {
     55          lowerBound = upperBound;
     56          //Interval is positive
     57        } else {
     58          upperBound = lowerBound;
     59        }
     60      }
     61
    4062      if (lowerBound > upperBound)
    4163        throw new ArgumentException("LowerBound must be smaller than UpperBound.");
     
    5880                double.IsNaN(LowerBound) || double.IsNaN(UpperBound);
    5981      }
     82    }
     83
     84    /// <summary>
     85    /// True if the interval is positive without zero
     86    /// </summary>
     87    public bool IsPositive {
     88      get => LowerBound > 0.0;
     89    }
     90
     91    /// <summary>
     92    /// True if the interval is negative without zero
     93    /// </summary>
     94    public bool IsNegative {
     95      get => UpperBound < 0.0;
    6096    }
    6197
     
    128164    }
    129165
    130     //mkommend: Division by intervals containing 0 is implemented as defined in
     166    //Division by intervals containing 0 is implemented as defined in
    131167    //http://en.wikipedia.org/wiki/Interval_arithmetic
    132168    public static Interval Divide(Interval a, Interval b) {
     
    171207    public static Interval Tangens(Interval a) {
    172208      return Interval.Divide(Interval.Sine(a), Interval.Cosine(a));
    173     } 
     209    }
    174210    public static Interval HyperbolicTangent(Interval a) {
    175211      return new Interval(Math.Tanh(a.LowerBound), Math.Tanh(a.UpperBound));
     
    206242      if (a.UpperBound <= 0) return new Interval(a.UpperBound * a.UpperBound, a.LowerBound * a.LowerBound);     // interval is negative
    207243      else if (a.LowerBound >= 0) return new Interval(a.LowerBound * a.LowerBound, a.UpperBound * a.UpperBound); // interval is positive
    208       else return new Interval(0, Math.Max(a.LowerBound*a.LowerBound, a.UpperBound*a.UpperBound)); // interval goes over zero
     244      else return new Interval(0, Math.Max(a.LowerBound * a.LowerBound, a.UpperBound * a.UpperBound)); // interval goes over zero
    209245    }
    210246
     
    235271      var absLower = Math.Abs(a.LowerBound);
    236272      var absUpper = Math.Abs(a.UpperBound);
    237       return new Interval(Math.Min(absLower, absUpper), Math.Max(absLower, absUpper));
     273      var min = Math.Min(absLower, absUpper);
     274      var max = Math.Max(absLower, absUpper);
     275
     276      if (a.Contains(0.0)) {
     277        min = 0.0;
     278      }
     279
     280      return new Interval(min, max);
    238281    }
    239282
    240283    public static Interval AnalyticalQuotient(Interval a, Interval b) {
    241       if (b.LowerBound <= 0 && b.UpperBound >= 0) //Second interval goes over zero or is zero
    242         return new Interval(a.LowerBound, a.UpperBound);
    243 
    244       var values = new List<double> {
    245         a.LowerBound / Math.Sqrt(Math.Pow(b.LowerBound, 2) + 1),
    246         a.LowerBound / Math.Sqrt(Math.Pow(b.UpperBound, 2) + 1),
    247         a.UpperBound / Math.Sqrt(Math.Pow(b.LowerBound, 2) + 1),
    248         a.UpperBound / Math.Sqrt(Math.Pow(b.UpperBound, 2) + 1)
    249       };
    250 
    251       return new Interval(values.Min(), values.Max());
     284      var dividend = a;
     285      var divisor = Add(Square(b), new Interval(1.0, 1.0));
     286      divisor = SquareRoot(divisor);
     287
     288      var quotient = Divide(dividend, divisor);
     289      return quotient;
    252290    }
    253291    #endregion
  • trunk/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/IntervalTest.cs

    r17348 r17350  
    1414    [TestCategory("Problems.DataAnalysis")]
    1515    [TestProperty("Time", "short")]
    16     public void TestIntervalAddOperation() {
     16    public void AddIntervalTest() {
    1717      //add        [x1,x2] + [y1,y2] = [x1 + y1,x2 + y2]
    1818
     
    2828    [TestCategory("Problems.DataAnalysis")]
    2929    [TestProperty("Time", "short")]
    30     public void TestIntervalSubOperation() {
     30    public void SubtractIntervalTest() {
    3131      //subtract   [x1,x2] − [y1,y2] = [x1 − y2,x2 − y1]
    3232
     
    4242    [TestCategory("Problems.DataAnalysis")]
    4343    [TestProperty("Time", "short")]
    44     public void TestIntervalMutlipyOperation() {
     44    public void MultiplyIntervalTest() {
    4545      //multiply   [x1,x2] * [y1,y2] = [min(x1*y1,x1*y2,x2*y1,x2*y2),max(x1*y1,x1*y2,x2*y1,x2*y2)]
    4646
     
    5959    [TestCategory("Problems.DataAnalysis")]
    6060    [TestProperty("Time", "short")]
    61     public void TestIntervalDivideOperation() {
     61    public void DivideIntervalTest() {
    6262      //divide  [x1, x2] / [y1, y2] = [x1, x2] * (1/[y1, y2]), where 1 / [y1,y2] = [1 / y2,1 / y1] if 0 not in [y_1, y_2].
    6363
     
    8080    [TestCategory("Problems.DataAnalysis")]
    8181    [TestProperty("Time", "short")]
    82     public void TestIntervalSineOperator() {
     82    public void SineIntervalTest() {
    8383      //sine depends on interval
    8484      //sin([0, 2*pi]) = [-1, 1]
     
    9898    [TestCategory("Problems.DataAnalysis")]
    9999    [TestProperty("Time", "short")]
    100     public void TestIntervalCosineOperator() {
     100    public void CosineIntervalTest() {
    101101      //Cosine uses sine Interval.Sine(Interval.Subtract(a, new Interval(Math.PI / 2, Math.PI / 2)));
    102102      Assert.AreEqual<Interval>(Interval.Cosine(new Interval(0, 2 * Math.PI)), new Interval(-1, 1));
     
    107107    [TestCategory("Problems.DataAnalysis")]
    108108    [TestProperty("Time", "short")]
    109     public void TestIntervalLogOperator() {
     109    public void LogIntervalTest() {
    110110      //Log([3, 5]) = [log(3), log(5)]
    111111      Assert.AreEqual<Interval>(new Interval(Math.Log(3), Math.Log(5)), Interval.Logarithm(new Interval(3, 5)));
     
    122122    [TestCategory("Problems.DataAnalysis")]
    123123    [TestProperty("Time", "short")]
    124     public void TestIntervalExpOperator() {
     124    public void ExponentialIntervalTest() {
    125125      //Exp([0, 1]) = [exp(0), exp(1)]
    126126      Assert.AreEqual<Interval>(new Interval(1, Math.Exp(1)), Interval.Exponential(new Interval(0, 1)));
     
    131131    [TestCategory("Problems.DataAnalysis")]
    132132    [TestProperty("Time", "short")]
    133     public void TestIntervalSqrOperator() {
     133    public void SquareIntervalTest() {
    134134      Assert.AreEqual<Interval>(new Interval(1, 4), Interval.Square(new Interval(1, 2)));
    135135      Assert.AreEqual<Interval>(new Interval(1, 4), Interval.Square(new Interval(-2, -1)));
     
    140140    [TestCategory("Problems.DataAnalysis")]
    141141    [TestProperty("Time", "short")]
    142     public void TestIntervalSqrtOperator() {
     142    public void SquarerootIntervalTest() {
    143143      Assert.AreEqual<Interval>(new Interval(1, 2), Interval.SquareRoot(new Interval(1, 4)));
    144144      Assert.AreEqual<Interval>(new Interval(double.NaN, double.NaN), Interval.SquareRoot(new Interval(-4, -1)));
     
    148148    [TestCategory("Problems.DataAnalysis")]
    149149    [TestProperty("Time", "short")]
    150     public void TestIntervalCubeOperator() {
     150    public void CubeIntervalTest() {
    151151      Assert.AreEqual<Interval>(new Interval(1, 8), Interval.Cube(new Interval(1, 2)));
    152152      Assert.AreEqual<Interval>(new Interval(-8, -1), Interval.Cube(new Interval(-2, -1)));
     
    157157    [TestCategory("Problems.DataAnalysis")]
    158158    [TestProperty("Time", "short")]
    159     public void TestIntervalCbrtOperator() {
     159    public void CubeRootIntervalTest() {
    160160      Assert.AreEqual<Interval>(new Interval(1, 2), Interval.CubicRoot(new Interval(1, 8)));
    161161      Assert.AreEqual<Interval>(new Interval(-2, -2), Interval.CubicRoot(new Interval(-8, -8)));
     
    170170    [TestCategory("Problems.DataAnalysis")]
    171171    [TestProperty("Time", "short")]
    172     public void TestIntervalAbsoluteOperator() {
    173       Assert.AreEqual(new Interval(2, 2), Interval.Absolute(new Interval(-2, -2)));
    174       Assert.AreEqual(new Interval(5, 5), Interval.Absolute(new Interval(5, 5)));
    175       Assert.AreEqual(new Interval(2, 8), Interval.Absolute(new Interval(2, 8)));
    176       Assert.AreEqual(new Interval(5, 14), Interval.Absolute(new Interval(-14, -5)));
    177       Assert.AreEqual(new Interval(2, 7), Interval.Absolute(new Interval(-2, 7)));
    178       Assert.AreEqual(new Interval(2, 22), Interval.Absolute(new Interval(-22, -2)));
    179       Assert.AreEqual(new Interval(6, 22), Interval.Absolute(new Interval(-22, 6)));
    180       Assert.AreEqual(new Interval(0, 0), Interval.Absolute(new Interval(0, 0)));
    181       Assert.AreEqual(new Interval(0, 2), Interval.Absolute(new Interval(-2, 0)));
    182       Assert.AreEqual(new Interval(0, 2), Interval.Absolute(new Interval(0, 2)));
    183     }
    184 
    185     [TestMethod]
    186     [TestCategory("Problems.DataAnalysis")]
    187     [TestProperty("Time", "short")]
    188     public void TestIntervalAnalyticalQuotient() {
     172    public void AbsoluteIntervalTest() {
     173      Assert.AreEqual(new Interval(2, 5), Interval.Absolute(new Interval(-5, -2)));
     174      Assert.AreEqual(new Interval(2, 5), Interval.Absolute(new Interval(2, 5)));
     175      Assert.AreEqual(new Interval(0, 3), Interval.Absolute(new Interval(-3, 0)));
     176      Assert.AreEqual(new Interval(0, 5), Interval.Absolute(new Interval(0, 5)));
     177      Assert.AreEqual(new Interval(0, 5), Interval.Absolute(new Interval(-2, 5)));
     178    }
     179
     180    [TestMethod]
     181    [TestCategory("Problems.DataAnalysis")]
     182    [TestProperty("Time", "short")]
     183    public void AnalyticalQuotientIntervalTest() {
     184      //Analytical Quotient ==> a / sqrt(b^2 + 1)
     185      var aPos = new Interval(3, 5);
     186      var aZero = new Interval(-3, 5);
     187      var aNeg = new Interval(-5, -3);
     188
     189      var bPos = new Interval(2, 4);
     190      var bZero = new Interval(-2, 4);
     191      var bNeg = new Interval(-4, -2);
     192
    189193      //Second interval goes over zero
    190       Assert.AreEqual(new Interval(-4, 2), Interval.AnalyticalQuotient(new Interval(-4, 2), new Interval(0, 5)));
    191       Assert.AreEqual(new Interval(-4, 2), Interval.AnalyticalQuotient(new Interval(-4, 2), new Interval(-4, 0)));
    192       Assert.AreEqual(new Interval(-4, 2), Interval.AnalyticalQuotient(new Interval(-4, 2), new Interval(-2, 4)));
    193       Assert.AreEqual(new Interval(-4, -2), Interval.AnalyticalQuotient(new Interval(-4, -2), new Interval(0, 5)));
    194       Assert.AreEqual(new Interval(-4, -2), Interval.AnalyticalQuotient(new Interval(-4, -2), new Interval(-4, 0)));
    195       Assert.AreEqual(new Interval(-4, -2), Interval.AnalyticalQuotient(new Interval(-4, -2), new Interval(-2, 4)));
    196       Assert.AreEqual(new Interval(3, 7), Interval.AnalyticalQuotient(new Interval(3, 7), new Interval(0, 5)));
    197       Assert.AreEqual(new Interval(3, 7), Interval.AnalyticalQuotient(new Interval(3, 7), new Interval(-4, 0)));
    198       Assert.AreEqual(new Interval(3, 7), Interval.AnalyticalQuotient(new Interval(3, 7), new Interval(-2, 4)));
    199       Assert.AreEqual(new Interval(0, 0), Interval.AnalyticalQuotient(new Interval(0, 0), new Interval(0, 5)));
    200       Assert.AreEqual(new Interval(-4, -4), Interval.AnalyticalQuotient(new Interval(-4, -4), new Interval(-4, 0)));
    201       Assert.AreEqual(new Interval(4, 4), Interval.AnalyticalQuotient(new Interval(4, 4), new Interval(-2, 4)));
    202 
    203       //First interval goes over zero
    204       Assert.AreEqual(new Interval(-2.0/Math.Sqrt(5), 5.0 / Math.Sqrt(5)), Interval.AnalyticalQuotient(new Interval(-2, 5), new Interval(-5, -2)));
    205       Assert.AreEqual(new Interval(-2.0 / Math.Sqrt(5), 5.0 / Math.Sqrt(5)), Interval.AnalyticalQuotient(new Interval(-2, 5), new Interval(2, 5)));
    206       Assert.AreEqual(new Interval(2.0/Math.Sqrt(26), 5.0/Math.Sqrt(5)), Interval.AnalyticalQuotient(new Interval(2, 5), new Interval(-5, -2)));
    207       Assert.AreEqual(new Interval(2.0 / Math.Sqrt(26), 5.0 / Math.Sqrt(5)), Interval.AnalyticalQuotient(new Interval(2, 5), new Interval(2, 5)));
    208       Assert.AreEqual(new Interval(-5.0/Math.Sqrt(5), -2.0/Math.Sqrt(26)), Interval.AnalyticalQuotient(new Interval(-5, -2), new Interval(-5, -2)));
    209       Assert.AreEqual(new Interval(-5.0 / Math.Sqrt(5), -2.0 / Math.Sqrt(26)), Interval.AnalyticalQuotient(new Interval(-5, -2), new Interval(2, 5)));
     194      Assert.AreEqual(new Interval(aPos.LowerBound/Math.Sqrt(17), aPos.UpperBound), Interval.AnalyticalQuotient(aPos, bZero));
     195      Assert.AreEqual(new Interval(aZero.LowerBound, aZero.UpperBound), Interval.AnalyticalQuotient(aZero, bZero));
     196      Assert.AreEqual(new Interval(aNeg.LowerBound, aNeg.UpperBound/Math.Sqrt(17)), Interval.AnalyticalQuotient(aNeg, bZero));
     197      //Second interval is positive
     198      Assert.AreEqual(new Interval(aPos.LowerBound/Math.Sqrt(17), aPos.UpperBound/Math.Sqrt(5)), Interval.AnalyticalQuotient(aPos, bPos));
     199      Assert.AreEqual(new Interval(aZero.LowerBound/Math.Sqrt(5), aZero.UpperBound/Math.Sqrt(5)), Interval.AnalyticalQuotient(aZero, bPos));
     200      Assert.AreEqual(new Interval(aNeg.LowerBound/Math.Sqrt(5), aNeg.UpperBound/Math.Sqrt(17)), Interval.AnalyticalQuotient(aNeg, bPos));
     201      //Second interval is negative
     202      Assert.AreEqual(new Interval(aPos.LowerBound/Math.Sqrt(17), aPos.UpperBound/Math.Sqrt(5)), Interval.AnalyticalQuotient(aPos, bNeg));
     203      Assert.AreEqual(new Interval(aZero.LowerBound/Math.Sqrt(5), aZero.UpperBound/Math.Sqrt(5)), Interval.AnalyticalQuotient(aZero, bNeg));
     204      Assert.AreEqual(new Interval(aNeg.LowerBound/Math.Sqrt(5), aNeg.UpperBound/Math.Sqrt(17)), Interval.AnalyticalQuotient(aNeg, bNeg));
     205    }
     206
     207    [TestMethod]
     208    [TestCategory("Problems.DataAnalysis")]
     209    [TestProperty("Time", "short")]
     210    public void IsNegativeIntervalTest() {
     211      Assert.IsTrue(new Interval(-2, -1).IsNegative);
     212      Assert.IsFalse(new Interval(-2, 0).IsNegative);
     213      Assert.IsFalse(new Interval(-2, 2).IsNegative);
     214      Assert.IsFalse(new Interval(2, 4).IsNegative);
     215    }
     216
     217    [TestMethod]
     218    [TestCategory("Problems.DataAnalysis")]
     219    [TestProperty("Time", "short")]
     220    public void IsPositiveIntervalTest() {
     221      Assert.IsTrue(new Interval(3, 5).IsPositive);
     222      Assert.IsFalse(new Interval(0, 5).IsPositive);
     223      Assert.IsFalse(new Interval(-1, 5).IsPositive);
     224      Assert.IsFalse(new Interval(-5, -2).IsPositive);
     225    }
     226
     227    [TestMethod]
     228    [TestCategory("Problems.DataAnalysis")]
     229    [TestProperty("Time", "short")]
     230    public void IsAlmostIntervalTest() {
     231      var negativeLowerBound = -2E-13;
     232      var negativeUpperBound = -1E-13;
     233      var positiveLowerBound = 3E-13;
     234      var positiveUpperBound = 5E-13;
     235
     236      var negativeInterval = new Interval(negativeLowerBound, negativeUpperBound);
     237      var positiveInterval = new Interval(positiveLowerBound, positiveUpperBound);
     238      var zeroInterval = new Interval(negativeUpperBound, positiveLowerBound);
     239
     240      //Check for right-shift of negative interval
     241      Assert.AreEqual(negativeUpperBound, negativeInterval.LowerBound);
     242      Assert.AreEqual(negativeUpperBound, negativeInterval.UpperBound);
     243      //Check for left-shift of positive interval
     244      Assert.AreEqual(positiveLowerBound, positiveInterval.LowerBound);
     245      Assert.AreEqual(positiveLowerBound, positiveInterval.UpperBound);
     246      //Check for setting interval to zero
     247      Assert.AreEqual(0, zeroInterval.LowerBound);
     248      Assert.AreEqual(0, zeroInterval.UpperBound);
     249
    210250    }
    211251  }
Note: See TracChangeset for help on using the changeset viewer.