Portability | portable |
---|---|
Stability | stable |
Maintainer | wren@community.haskell.org |
This module presents a type for storing numbers in the log-domain. The main reason for doing this is to prevent underflow when multiplying many small probabilities as is done in Hidden Markov Models and other statistical models often used for natural language processing. The log-domain also helps prevent overflow when multiplying many large numbers. In rare cases it can speed up numerical computation (since addition is faster than multiplication, though logarithms are exceptionally slow), but the primary goal is to improve accuracy of results. A secondary goal has been to maximize efficiency since these computations are frequently done within a O(n^3) loop.
The LogFloat
of this module is restricted to non-negative
numbers for efficiency's sake, see the forthcoming
Data.Number.LogFloat.Signed for doing signed log-domain
calculations.
- log :: (Floating a, Transfinite a) => a -> a
- toFractional :: (Real a, Fractional b) => a -> b
- data LogFloat
- logFloat :: Real a => a -> LogFloat
- logToLogFloat :: Real a => a -> LogFloat
- fromLogFloat :: (Fractional a, Transfinite a) => LogFloat -> a
- logFromLogFloat :: (Fractional a, Transfinite a) => LogFloat -> a
- module Data.Number.Transfinite
Basic functions
log :: (Floating a, Transfinite a) => a -> aSource
Since the normal log
throws an error on zero, we
have to redefine it in order for things to work right. Arguing
from limits we can see that log 0 == negativeInfinity
. Newer
versions of GHC have this behavior already, but older versions
and Hugs do not.
This function will raise an error when taking the log of negative
numbers, rather than returning notANumber
as the newer GHC
implementation does. The reason being that typically this is a
logical error, and notANumber
allows the error to propegate
silently.
In order to improve portability, the Transfinite
class is
required to indicate that the Floating
type does in fact have
a representation for negative infinity. Both native floating
types (Double
and Float
) are supported. If you define your
own instance of Transfinite
, verify the above equation holds
for your 0
and negativeInfinity
. If it doesn't, then you
should avoid importing our log
and will probably want converters
to handle the discrepancy when dealing with LogFloat
s.
toFractional :: (Real a, Fractional b) => a -> bSource
The most generic numeric converter I can come up with. All the
built-in numeric types are Real
, though Int
and Integer
aren't Fractional
. Beware that converting transfinite values
into Ratio
types is error-prone and non-portable, as discussed
in Data.Number.Transfinite.
LogFloat
data type and conversion functions
A LogFloat
is just a Double
with a special interpretation.
The logFloat
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 so will
underflow at that point. This behavior may change in the future.
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 * logFloat q * logFloat r
it's faster to do lp * logFloat
(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.
Even more particularly, you should avoid addition whenever
possible. Addition is provided because it's necessary at times
and the proper implementation is not immediately transparent.
However, between two LogFloat
s addition requires crossing the
exp/log boundary twice; with a LogFloat
and a regular number
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 plain operations first, do it!
logFloat :: Real a => a -> LogFloatSource
A constructor which does semantic conversion from normal-domain to log-domain.
logToLogFloat :: Real a => a -> LogFloatSource
Constructor which assumes the argument is already in the log-domain.
fromLogFloat :: (Fractional a, Transfinite a) => LogFloat -> aSource
Return our log-domain value back into normal-domain. Beware of overflow/underflow.
logFromLogFloat :: (Fractional a, Transfinite a) => LogFloat -> aSource
Return the log-domain value itself without costly conversion
Exceptional numeric values
module Data.Number.Transfinite