линейная алгебра произвольной точности - PullRequest
15 голосов
/ 29 июля 2011

У меня есть двумерный массив [средний / большой размер, скажем, 500x500].Я хочу найти собственные значения поэлементного показателя этого.Проблема заключается в том, что некоторые значения являются довольно отрицательными (-800, -1000 и т. Д.), А их показатели недооценены (это означает, что они настолько близки к нулю, что numpy рассматривает их как ноль).Можно ли как-нибудь использовать произвольную точность в numpy?

То, о чем я мечтаю:

import numpy as np

np.set_precision('arbitrary') # <--- Missing part
a = np.array([[-800.21,-600.00],[-600.00,-1000.48]])
ex = np.exp(a)  ## Currently warns about underflow
eigvals, eigvecs = np.linalg.eig(ex)

Я искал решение с помощью gmpy и mpmath безрезультатно.Любая идея будет приветствоваться.

Ответы [ 5 ]

15 голосов
/ 30 июля 2011

SymPy может вычислить произвольную точность:

from sympy import exp, N, S
from sympy.matrices import Matrix

data = [[S("-800.21"),S("-600.00")],[S("-600.00"),S("-1000.48")]]
m = Matrix(data)
ex = m.applyfunc(exp).applyfunc(lambda x:N(x, 100))
vecs = ex.eigenvects()
print vecs[0][0] # eigen value
print vecs[1][0] # eigen value
print vecs[0][2] # eigen vect
print vecs[1][2] # eigen vect

вывод:

-2.650396553004310816338679447269582701529092549943247237903254759946483528035516341807463648841185335e-261
2.650396553004310816338679447269582701529092549943247237903254759946483528035516341807466621962539464e-261
[[-0.9999999999999999999999999999999999999999999999999999999999999999999999999999999999999994391176386872]
[                                                                                                      1]]
[[1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000560882361313]
[                                                                                                    1]]

вы можете изменить 100 в N (x, 100) на другую точность, но, как я пытался 1000, расчет собственных векторов не удался.

10 голосов
/ 30 июля 2011

В 64-битных системах существует тип numpy.float128 d. (Я полагаю, что в 32-битных системах также существует тип float96 d). Хотя numpy.linalg.eig не поддерживает 128-битные операции с плавающей запятой, scipy.linalg.eig (вроде) поддерживает.

Однако, ничего из этого не имеет значения , в конечном счете. Любой общий решатель для задачи на собственные значения будет итеративным, а не точным, так что вы ничего не получите, сохраняя дополнительную точность! np.linalg.eig работает для любой фигуры, но никогда не возвращает точное решение.

Если вы всегда решаете матрицы 2x2, написать свой собственный решатель, который должен быть более точным, тривиально. Я покажу пример этого в конце ...

Независимо от того, продвигаясь вперед в бессмысленно точных контейнерах памяти:

import numpy as np
import scipy as sp
import scipy.linalg

a = np.array([[-800.21,-600.00],[-600.00,-1000.48]], dtype=np.float128)
ex = np.exp(a)
print ex

eigvals, eigvecs = sp.linalg.eig(ex)

# And to test...
check1 = ex.dot(eigvecs[:,0])
check2 = eigvals[0] * eigvecs[:,0]
print 'Checking accuracy..'
print check1, check2
print (check1 - check2).dot(check1 - check2), '<-- Should be zero'

Однако вы заметите, что то, что вы получаете, идентично тому, что вы делаете np.linalg.eig(ex.astype(np.float64). На самом деле, я вполне уверен, что именно это делает scipy, тогда как numpy вызывает ошибку, а не делает это молча Хотя я могу ошибаться ...

Если вы не хотите использовать scipy, одним из обходных путей является изменение масштаба вещей после возведения в степень, но перед поиском для собственных значений, приведение их к «нормальным» числам с плавающей запятой, решение для собственных значений, а затем преобразование вещей в float128 впоследствии и отмасштабировать.

1026 * Е.Г. *

import numpy as np

a = np.array([[-800.21,-600.00],[-600.00,-1000.48]], dtype=np.float128)
ex = np.exp(a)
factor = 1e300
ex_rescaled = (ex * factor).astype(np.float64)

eigvals, eigvecs = np.linalg.eig(ex_rescaled)
eigvals = eigvals.astype(np.float128) / factor

# And to test...
check1 = ex.dot(eigvecs[:,0])
check2 = eigvals[0] * eigvecs[:,0]
print 'Checking accuracy..'
print check1, check2
print (check1 - check2).dot(check1 - check2), '<-- Should be zero'

Наконец, если вы решаете только матрицы 2x2 или 3x3, вы можете написать свой собственный решатель, который будет возвращать точное значение для этих форм матриц.

import numpy as np

def quadratic(a,b,c):
    sqrt_part = np.lib.scimath.sqrt(b**2 - 4*a*c)
    root1 = (-b + sqrt_part) / (2 * a)
    root2 = (-b - sqrt_part) / (2 * a)
    return root1, root2

def eigvals(matrix_2x2):
    vals = np.zeros(2, dtype=matrix_2x2.dtype)
    a,b,c,d = matrix_2x2.flatten()
    vals[:] = quadratic(1.0, -(a+d), (a*d-b*c))
    return vals

def eigvecs(matrix_2x2, vals):
    a,b,c,d = matrix_2x2.flatten()
    vecs = np.zeros_like(matrix_2x2)
    if (b == 0.0) and (c == 0.0):
        vecs[0,0], vecs[1,1] = 1.0, 1.0
    elif c != 0.0:
        vecs[0,:] = vals - d
        vecs[1,:] = c
    elif b != 0:
        vecs[0,:] = b
        vecs[1,:] = vals - a
    return vecs

def eig_2x2(matrix_2x2):
    vals = eigvals(matrix_2x2)
    vecs = eigvecs(matrix_2x2, vals)
    return vals, vecs

a = np.array([[-800.21,-600.00],[-600.00,-1000.48]], dtype=np.float128)
ex = np.exp(a)
eigvals, eigvecs =  eig_2x2(ex) 

# And to test...
check1 = ex.dot(eigvecs[:,0])
check2 = eigvals[0] * eigvecs[:,0]
print 'Checking accuracy..'
print check1, check2
print (check1 - check2).dot(check1 - check2), '<-- Should be zero'

Это возвращает действительно точное решение, но будет работать только для матриц 2x2. Однако это единственное решение, которое на самом деле выигрывает от дополнительной точности!

8 голосов
/ 29 июля 2011

Насколько я знаю, numpy не поддерживает точность выше двойной (float64), которая является значением по умолчанию, если не указано.

Попробуйте использовать это: http://code.google.com/p/mpmath/

Списокособенности (среди прочих)

Арифметика:

  • Действительные и комплексные числа с произвольной точностью
  • Неограниченные размеры / величины экспоненты
0 голосов
/ 29 июля 2011

Так что вам нужно 350 цифр точности. Вы не получите этого с числами с плавающей точкой IEEE (что использует numpy). Вы можете получить его с помощью программы bc:

$ bc -l
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
scale=350
e(-800)
.<hundreds of zeros>00366
0 голосов
/ 29 июля 2011

У меня нет опыта работы с numpy, но добавление десятичной точки с установленным количеством нулей может помочь. Например, используйте 1.0000 вместо 1. В обычных скриптах на python, где у меня была эта проблема, это помогло, поэтому, если ваша проблема не вызвана странностью в numpy и не имеет ничего общего с python, это должно помочь.

Удачи!

...