| Safe Haskell | None | 
|---|---|
| Language | Haskell2010 | 
NumHask.Data.LogField
Synopsis
- data LogField a
- logField :: ExpField a => a -> LogField a
- fromLogField :: ExpField a => LogField a -> a
- logToLogField :: a -> LogField a
- logFromLogField :: LogField a -> a
- accurateSum :: (ExpField a, Foldable f, Ord a) => f (LogField a) -> LogField a
- accurateProduct :: (ExpField a, Foldable f) => f (LogField a) -> LogField a
- pow :: (ExpField a, LowerBoundedField a, Ord a) => LogField a -> a -> LogField a
LogField
A LogField is just a Field with a special interpretation.
 The LogField function is presented instead of the constructor,
 in order to ensure semantic conversion. At present the Show
 instance will convert back to the normal-domain, and hence will
 underflow at that point. This behavior may change in the future.
Because logField performs the semantic conversion, we can use
 operators which say what we *mean* rather than saying what we're
 actually doing to the underlying representation. That is,
 equivalences like the following are true[1] thanks to type-class
 overloading:
logField (p + q) == logField p + logField q logField (p * q) == logField p * logField q
Performing operations in the log-domain is cheap, prevents
 underflow, and is otherwise very nice for dealing with miniscule
 probabilities. However, crossing into and out of the log-domain
 is expensive and should be avoided as much as possible. In
 particular, if you're doing a series of multiplications as in
 lp * LogField q * LogField r it's faster to do lp * LogField
 (q * r) if you're reasonably sure the normal-domain multiplication
 won't underflow; because that way you enter the log-domain only
 once, instead of twice. Also note that, for precision, if you're
 doing more than a few multiplications in the log-domain, you
 should use product rather than using (*) repeatedly.
Even more particularly, you should avoid addition whenever
 possible. Addition is provided because sometimes we need it, and
 the proper implementation is not immediately apparent. However,
 between two LogFields addition requires crossing the exp/log
 boundary twice; with a LogField and a Double it's three
 times, since the regular number needs to enter the log-domain
 first. This makes addition incredibly slow. Again, if you can
 parenthesize to do normal-domain operations first, do it!
- 1
- That is, true up-to underflow and floating point fuzziness. Which is, of course, the whole point of this module.
Instances
logField :: ExpField a => a -> LogField a Source #
Constructor which does semantic conversion from normal-domain
 to log-domain. Throws errors on negative and NaN inputs. If p
 is non-negative, then following equivalence holds:
logField p == logToLogField (log p)
fromLogField :: ExpField a => LogField a -> a Source #
Semantically convert our log-domain value back into the normal-domain. Beware of overflow/underflow. The following equivalence holds (without qualification):
fromLogField == exp . logFromLogField
Isomorphism to log-domain
logToLogField :: a -> LogField a Source #
Constructor which assumes the argument is already in the log-domain.
logFromLogField :: LogField a -> a Source #
Return the log-domain value itself without conversion.
Additional operations
accurateSum :: (ExpField a, Foldable f, Ord a) => f (LogField a) -> LogField a Source #
O(n). Compute the sum of a finite list of LogFields, being
 careful to avoid underflow issues. That is, the following
 equivalence holds (modulo underflow and all that):
LogField . accurateSum == accurateSum . map LogField
N.B., this function requires two passes over the input. Thus, it is not amenable to list fusion, and hence will use a lot of memory when summing long lists.
accurateProduct :: (ExpField a, Foldable f) => f (LogField a) -> LogField a Source #
O(n). Compute the product of a finite list of LogFields,
 being careful to avoid numerical error due to loss of precision.
 That is, the following equivalence holds (modulo underflow and
 all that):
LogField . accurateProduct == accurateProduct . map LogField