Come aggiungere in modo affidabile termini esponenziali di grandi dimensioni senza errori di overflow?


24

Un problema molto comune nella catena di Markov Monte Carlo riguarda le probabilità di calcolo che sono la somma di termini esponenziali di grandi dimensioni,

ea1+ea2+...

dove i componenti di a lattina possono variare da molto piccoli a molto grandi. Il mio approccio è stato quello di fattorizzare il termine esponenziale più grande modo che:K:=maxi(ai)

a=K+log(ea1K+ea2K+...)
eaea1+ea2+...

Questo approccio è ragionevole se tutti gli elementi di a sono grandi, ma non è una buona idea se non lo sono. Naturalmente, gli elementi più piccoli non contribuiscono comunque alla somma in virgola mobile, ma non sono sicuro di come gestirli in modo affidabile. Nel codice R, il mio approccio è simile a:

if ( max(abs(a)) > max(a) )
  K <-  min(a)
else
  K <- max(a)
ans <- log(sum(exp(a-K))) + K

Sembra un problema abbastanza comune che ci dovrebbe essere una soluzione standard, ma non sono sicuro di cosa si tratti. Grazie per eventuali suggerimenti.


1
Questa è una cosa Google per "logsumexp".

Risposte:


15

Esiste una soluzione semplice con solo due passaggi tra i dati:

Primo calcolo

K:=maxiai,

che ti dice che, se ci sono termini, allora Σ i e un in e K .n

ieaineK.

Dato che presumibilmente non hai nessun punto vicino pari a 10 20 , non dovresti preoccuparti di traboccare nel calcolo di τ : = i e a i - Knn1020

τ:=ieaiKn
in doppia precisione.

Quindi, calcola e quindi la tua soluzione è e K τ .τeKτ


Grazie per la chiara notazione - ma credo che questo sia essenzialmente quello che ho proposto (?) Se devo evitare errori di underflow quando alcuni sono piccoli, mi accorgo di aver bisogno dell'approccio di somma Kahan proposto da @gareth ? ai
cboettig,

Ah, ora vedo a cosa stavi arrivando. In realtà non devi preoccuparti di underflow, poiché l'aggiunta di risultati eccezionalmente piccoli alla tua soluzione non dovrebbe modificarlo. Se ce n'era un numero eccezionalmente elevato, dovresti prima sommare i valori piccoli.
Jack Poulson il

Al downvoter: ti dispiacerebbe farmi sapere cosa c'è che non va nella mia risposta?
Jack Poulson,

eaiK0


10

Per mantenere la precisione mentre si sommano i doppi, è necessario utilizzare Kahan Summation , questo è il software equivalente ad avere un registro di trasporto.

e709.783doubleMax - sumSoFar < valueToAddexponent > 709.783

value×2shift

#!/usr/bin/env python
from math import exp, log, ceil

doubleMAX = (1.0 + (1.0 - (2 ** -52))) * (2 ** (2 ** 10 - 1))

def KahanSumExp(expvalues):
  expvalues.sort() # gives precision improvement in certain cases 
  shift = 0 
  esum = 0.0 
  carry = 0.0 
  for exponent in expvalues:
    if exponent - shift * log(2) > 709.783:
      n = ceil((exponent - shift * log(2) - 709.783)/log(2))
      shift += n
      carry /= 2*n
      esum /= 2*n
    elif exponent - shift * log(2) < -708.396:
      n = floor((exponent - shift * log(2) - -708.396)/log(2))
      shift += n
      carry *= 2*n
      esum *= 2*n
    exponent -= shift * log(2)
    value = exp(exponent) - carry 
    if doubleMAX - esum < value:
      shift += 1
      esum /= 2
      value /= 2
    tmp = esum + value 
    carry = (tmp - esum) - value 
    esum = tmp
  return esum, shift

values = [10, 37, 34, 0.1, 0.0004, 34, 37.1, 37.2, 36.9, 709, 710, 711]
value, shift = KahanSumExp(values)
print "{0} x 2^{1}".format(value, shift)

La somma di Kahan non fa che parte di una famiglia di metodi di "somma sommatoria compensata". Se per qualche motivo Kahan non funziona abbastanza bene, ci sono molti altri metodi per sommare correttamente termini di grandezza variabile e segni opposti.
JM

@JM potresti fornirmi i nomi di quegli altri metodi, sarei piuttosto interessato a leggerli. Grazie.
Gareth A. Lloyd,


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.