ekmett/intervals

Definition of mignitude

Closed this issue · 14 comments

Could you clarify the definition of mignitude?

-- | \"mignitude\"
--
-- >>> mignitude (1 ... 20)
-- 1
--
-- >>> mignitude (-20 ... 10)
-- 10
--
-- >>> mignitude (singleton 5)
-- 5
--
-- >>> mignitude empty
-- 0
mignitude :: (Num a, Ord a) => Interval a -> a
mignitude (I a b) = on min abs a b
mignitude Empty = 0
{-# INLINE mignitude #-}

I expected the answer to the second test case, mignitude (-20 ... 10), to be 0, not 10, because that is the smallest number that could be in the interval.

mignitude i = let (I a b) = abs i
               in on min abs a b

Trying to google an official definition isn't getting me very far. Though I did find a couple of libraries for other languages that take my view of that test case, e.g. https://mvngu.googlecode.com/hg/onepage/sage/rings/real_mpfi/sage.rings.real_mpfi.RealIntervalFieldElement.mignitude.html.

Whoops, I think my try doesn't match my definition either.

mignitude = inf . abs

Relatedly, it seems to me that magnitude Empty should be nan and not 0.

I'd be okay with taking the smallest absolute value in the interval as its definition.

I have no real preference re NaN vs 0. Your preference trumps my apathy. Make it so. ;)

One benefit of 0 is that it means it is defined for all Num, not just Fractional though.

Yeah, I agree that the extra context is a drawback. I suppose it doesn't matter to me, really. Except that it seems wrong to use the inf . abs definition/implementation if inf Empty = nan without changing the Empty case. I can't come up with a justification for using 0 other than avoiding the context.

Maybe inf Empty is actually undefined instead of the trick of pulling in nan through Fractional? The nan trick is really nice for Interval Double, but really isn't much different from undefined for other Fractional types. That seems like a big change though.

Switching to a separate exception type across the board could be problematic, NaN is quiet. exceptions blow up sky high, but perhaps that'd be an acceptable price.

I could see building an EmptyIntervalException and throwing it as a more robustly catchable form of error, after all these inf/sup cases for Empty shouldn't be used, so we can yell sanely I guess. I already cleaned up the code so we don't use inf and sup internally very much.

I was just writing it up that way to see how it looked, but I don't know how to rewrite the doctest for the case that would now be an error case.

I don't know anything about haskell's exception system apart from writing error "Empty interval". I will read up I guess.

Oh, and I guess what I was getting at is that NaN is quiet, but nan is only quiet when it is at a type where there is a quiet NaN, so the problem of it blowing up sky high (sort of) already exists.

inf (Empty :: Interval Rational) is "*** Exception: Ratio has zero denominator".

Just pushed something. Take a look and see what you think.

OK, good. Hold on a minute, I have a few tweaks on top of that that I will make mergeable with it.

Should there be a related change to this, I can't tell:

-- | 'realToFrac' will use the midpoint
instance Real a => Real (Interval a) where
  toRational Empty = nan
  toRational (I ra rb) = a + (b - a) / 2 where
    a = toRational ra
    b = toRational rb
  {-# INLINE toRational #-}

Yes, that should also throw EmptyInterval. I changed it in the other type. Missed one apparently.

Looks fixed to me.