Calcolo efficiente dell'inverso della radice quadrata della matrice


15

Un problema comune nelle statistiche è il calcolo dell'inverso della radice quadrata di una matrice definita positiva simmetrica. Quale sarebbe il modo più efficiente per calcolare questo?

Mi sono imbattuto in alcuni articoli letterari (che non ho ancora letto) e in alcuni codici R casuali qui , che riprodurrò qui per comodità

# function to compute the inverse square root of a matrix
fnMatSqrtInverse = function(mA) {
  ei = eigen(mA)
  d = ei$values
      d = (d+abs(d))/2
      d2 = 1/sqrt(d)
      d2[d == 0] = 0
      return(ei$vectors %*% diag(d2) %*% t(ei$vectors))
}

Non sono del tutto sicuro di aver compreso la linea d = (d+abs(d))/2. Esiste un modo più efficiente di calcolare l'inverso della radice quadrata della matrice? La eigenfunzione R chiama LAPACK .


d(d+|d|)/2A - 1 / 2max(d,0)UN-1/2UN-1/2X

@DanielShapero Grazie per il tuo commento. Quindi se ho una matrice PSD, non ho bisogno di quella linea? La mia applicazione richiede il calcolo di forme quadratiche come . UN-1/2BUN-1/2
Tchakravarty,

Non ho familiarità con R, ma data la linea 7 presumo che abbia indicizzazione logica come Matlab. In tal caso, ti suggerisco di riscrivere la riga 5 come d[d<0] = 0, che è più espressiva.
Federico Poloni,

Questo codice è corretto? L'ho eseguito su un semplice esempio in matlab e ho trovato la risposta sbagliata. La mia matrice è definita positiva ma sicuramente non simmetrica. Si prega di vedere la mia risposta qui sotto: ho trasferito il codice su matlab.
roni,

Risposte:


10

Il codice che hai pubblicato utilizza la decomposizione autovalore della matrice simmetrica per calcolare . UN-1/2

La dichiarazione

d = (d + abs (d)) / 2

accetta effettivamente qualsiasi voce negativa in d e la imposta su 0, lasciando solo le voci non negative. Cioè, qualsiasi autovalore negativo di viene trattato come se fosse 0. In teoria, gli autovalori di A dovrebbero essere tutti non negativi, ma in pratica è comune vedere piccoli autovalori negativi quando si calcolano gli autovalori di un definito apparentemente positivo matrice di covarianza quasi singolare. UN

Se hai davvero bisogno dell'inverso della radice quadrata della matrice simmetrica di , e è ragionevolmente piccolo (non più grande di dire 1.000 per 1.000), allora è buono quasi quanto qualsiasi metodo tu possa usare. AUNUN

In molti casi è invece possibile utilizzare un fattore di Cholesky dell'inverso della matrice di covarianza (o praticamente lo stesso, il fattore di Cholesky della matrice di covarianza stessa). Il calcolo del fattore di Cholesky è in genere un ordine di grandezza più veloce rispetto al calcolo della decomposizione autovalore per matrici dense e molto più efficienti (sia in termini di tempo di calcolo che di archiviazione richiesta) per matrici grandi e sparse. Pertanto, l'uso della fattorizzazione di Cholesky diventa molto desiderabile quando è grande e radi. UN


6
La risposta di Brian dà buoni consigli: usa invece il fattore Cholesky (se puoi). C'è un altro ottimizzazione che si può fare per di più: non calcolare la PSD matrice . Spesso si ottiene da un calcolo come , con rettangolare. In questo caso, è sufficiente calcolare una decomposizione QR di per ottenere il fattore Cholesky di , con una precisione molto migliore. A A = B T B B B R AUNUNUN=BTBBBRUN
Federico Poloni,

5

Nella mia esperienza, il metodo polare-Newton di Higham funziona molto più velocemente (vedi il capitolo 6 delle funzioni delle matrici di N. Higham). In questa mia breve nota ci sono trame che confrontano questo metodo con i metodi del primo ordine. Inoltre, vengono presentate citazioni a molti altri approcci a matrice quadrata-radice, sebbene principalmente l'iterazione polare di Newton sembra funzionare meglio (ed evita di fare calcoli di autovettori).

% compute the matrix square root; modify to compute inverse root.
function X = PolarIter(M,maxit,scal)
  fprintf('Running Polar Newton Iteration\n');
  skip = floor(maxit/10);
  I = eye(size(M));
  n=size(M,1);
  if scal
    tm = trace(M);
    M  = M / tm;
  else
    tm = 1;
  end
  nm = norm(M,'fro');

  % to compute inv(sqrt(M)) make change here
  R=chol(M+5*eps*I);

  % computes the polar decomposition of R
  U=R; k=0;
  while (k < maxit)
    k=k+1;
    % err(k) = norm((R'*U)^2-M,'fro')/nm;
    %if (mod(k,skip)==0)
    %  fprintf('%d: %E\n', k, out.err(k));
    %end

    iU=U\I;
    mu=sqrt(sqrt(norm(iU,1)/norm(U,1)*norm(iU,inf)/norm(U,inf)));
    U=0.5*(mu*U+iU'/mu);

   if (err(k) < 1e-12), break; end
  end
  X=sqrt(tm)*R'*U;
  X = 0.5*(X+X');
end

0

Ottimizza il tuo codice:

Opzione 1 - Ottimizza il tuo codice R:
a. È possibile apply()una funzione per dquella volontà sia max(d,0)e d2[d==0]=0in un loop.
b. Prova a operare ei$valuesdirettamente.

Opzione 2 - Usa C ++:
riscrivi l'intera funzione in C ++ con RcppArmadillo. Sarai ancora in grado di chiamarlo da R.

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.