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.
|If you see no module description above, then the lhs2hs
script was not run correctly. Please rebuild the documentation
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.
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 LogFloats.
|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
|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
LogFloats 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!
|A constructor which does semantic conversion from normal-domain
|Constructor which assumes the argument is already in the log-domain.
|Return our log-domain value back into normal-domain. Beware of
|Return the log-domain value itself without costly conversion
|Exceptional numeric values
|Produced by Haddock version 2.4.2|