Como adicionar termos exponenciais grandes de forma confiável, sem erros de estouro?

24

Um problema muito comum na cadeia de Markov Monte Carlo envolve probabilidades de computação que são soma de grandes termos exponenciais,

ea1+ea2+...

onde os componentes de lata podem variar de muito pequeno a muito grande. Minha abordagem foi fatorar o maior termo exponencial para que:K : = max i ( a i )aK:=maxi(ai)

e um 'e um 1 + um e um 2 + . . .

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

Essa abordagem é razoável se todos os elementos de uma forem grandes, mas não é uma boa ideia se não forem. Obviamente, os elementos menores não estão contribuindo para a soma de ponto flutuante, mas não tenho certeza de como lidar com eles de maneira confiável. No código R, minha abordagem se parece com:

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

Parece um problema bastante comum que deve haver uma solução padrão, mas não tenho certeza do que seja. Obrigado por todas as sugestões.

cboettig
fonte
1
Isso é uma coisa. Google para 'logsumexp'.

Respostas:

15

Existe uma solução direta com apenas duas passagens pelos dados:

Primeiro calcule

K: =maxEuumaEu,

que informa que, se houver termos, então Σ i e um iN e K .n

EueumaEuneK.

Como você presumivelmente não tem nem tão grande quanto , não deve se preocupar em transbordar no cálculo de em dupla precisão .10 20 τ : = Σ i e um i - Knn1020

τ: =EueumaEu-Kn

Portanto, calcule e sua solução será .e K ττeKτ

Jack Poulson
fonte
Obrigado pela notação clara - mas acredito que isso é essencialmente o que eu propus (?) Se eu precisar evitar erros de fluxo insuficiente quando alguns forem pequenos, concluo que preciso da abordagem de soma Kahan proposta por @gareth ? umaEu
C2f2
Ah, agora eu vejo no que você estava falando. Você realmente não precisa se preocupar com o fluxo insuficiente, pois adicionar resultados excepcionalmente pequenos à sua solução não deve alterá-lo. Se houver um número excepcionalmente grande deles, você deve somar os valores pequenos primeiro.
Jack Poulson
Para o downvoter: você se importaria em me informar o que há de errado com a minha resposta?
22412 Jack Poulson
e se você tiver muitos termos muito pequenos? Pode acontecer que para eles. Se houver muitos termos como esse, você terá um grande erro. eumaEu-K0 0
Becko
10

Para manter a precisão enquanto você adiciona duplas, você precisa usar o Kahan Summation , este é o software equivalente a ter um registro de transporte.

Isso é bom para a maioria dos valores, mas se você está recebendo estouro, em seguida, você está batendo os limites da IEEE 754 de precisão dupla que seria cerca . Neste ponto, você precisa de uma nova representação. Você pode detectar um estouro no momento da adição e também detectar expoentes grandes para avaliar . Nesse ponto, você pode modificar a interpretação de um duplo deslocando o expoente e acompanhando esse deslocamento.e709.783doubleMax - sumSoFar < valueToAddexponent > 709.783

vumaeuvocêe×2shEuft

#!/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)
Gareth A. Lloyd
fonte
O somatório de Kahan é apenas um de uma família de métodos de "somatório compensado". Se, por algum motivo, Kahan não funcionar corretamente, existem vários outros métodos para adicionar termos de magnitudes variadas e sinais opostos corretamente.
JM
@JM, você poderia me fornecer os nomes desses outros métodos? Eu ficaria muito interessado em ler sobre eles. Obrigado.
Gareth A. Lloyd
1

Sua abordagem é sólida.

KK

John D. Cook
fonte
0

Existe um pacote R que fornece uma implementação rápida e eficiente do "truque log-soma-exp"

http://www.inside-r.org/packages/cran/matrixStats/docs/logSumExp

A função logSumExp aceita um vetor numérico lX e gera log (soma (exp (lX))), evitando problemas de estouro e estouro usando o método que você descreveu.

kantai
fonte