2011-05-04 76 views
24

Estoy tratando de encontrar el espacio nulo (espacio solución de Ax = 0) de una matriz dada. Encontré dos ejemplos, pero parece que tampoco puedo trabajar. Además, no puedo entender lo que están haciendo para llegar allí, así que no puedo depurar. Espero que alguien pueda ayudarme a superar esto.Python (NumPy, SciPy), encontrar el espacio nulo de una matriz

Las páginas de documentación (numpy.linalg.svd y numpy.compress) son opacas para mí. Aprendí a hacer esto creando la matriz C = [A|0], encontrando la forma de escala escalonada reducida y resolviendo las variables por fila. Parece que no puedo seguir cómo se está haciendo en estos ejemplos.

¡Gracias por toda la ayuda!

Aquí es mi matriz de la muestra, que es el mismo que el wikipedia example: Método

A = matrix([ 
    [2,3,5], 
    [-4,2,3] 
    ]) 

(found here y here):

import scipy 
from scipy import linalg, matrix 
def null(A, eps=1e-15): 
    u, s, vh = scipy.linalg.svd(A) 
    null_mask = (s <= eps) 
    null_space = scipy.compress(null_mask, vh, axis=0) 
    return scipy.transpose(null_space) 

Cuando lo intento, le regreso una lata vacía matriz:

Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56) 
[GCC 4.4.5] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import scipy 
>>> from scipy import linalg, matrix 
>>> def null(A, eps=1e-15): 
... u, s, vh = scipy.linalg.svd(A) 
... null_mask = (s <= eps) 
... null_space = scipy.compress(null_mask, vh, axis=0) 
... return scipy.transpose(null_space) 
... 
>>> A = matrix([ 
...  [2,3,5], 
...  [-4,2,3] 
...  ]) 
>>> 
>>> null(A) 
array([], shape=(3, 0), dtype=float64) 
>>> 
+2

la página de Wikipedia se ha vinculado a da en realidad una Muy buena explicación de por qué debería usar una SVD para calcular el espacio nulo (o resolver) de una matriz cuando se trata de valores de coma flotante. http://en.wikipedia.org/wiki/Kernel_%28matrix%29#Numerical_computation_of_null_space El enfoque que describes (resolver las variables fila por fila) amplificará cualquier error de redondeo, etc. (Esta es la misma razón por la que deberías casi nunca calcule explícitamente el inverso de una matriz ...) –

Respuesta

5

Parece estar funcionando bien para mí:

A = matrix([[2,3,5],[-4,2,3],[0,0,0]]) 
A * null(A) 
>>> [[ 4.02455846e-16] 
>>> [ 1.94289029e-16] 
>>> [ 0.00000000e+00]] 
+0

Estoy seguro de que me falta algo, pero Wikipedia sugiere que los valores deberían ser '[[-.0625], [-1.625], [1]]'? –

+0

Además, me devuelve una matriz vacía '[]'. ¿Qué podría estar mal? –

+6

@Nona Urbiz - Devuelve una matriz vacía porque no está poniendo una fila de ceros, como hace Bashwork (y wikipedia) arriba. Además, los valores de espacio nulo devueltos ('[-0.33, -0.85, 0.52]') están normalizados de modo que la magnitud del vector es 1. El ejemplo de wikipedia no está normalizado. Si simplemente toma 'n = null (A)' y echa un vistazo a 'n/n.max()', obtendrá '[-.0625, -1.625, 1]'. –

9

Obtiene la descomposición SVD del matriz A. s es un vector de valores propios. Usted está interesado en casi sin valores propios (véase $ A * x = \ lambda * x $ donde $ \ ABS (\ lambda) < \ epsilon $), que viene dada por el vector de valores lógicos null_mask.

Luego, extrae de la lista vh los vectores propios correspondientes a los autovalores casi cero, que es exactamente lo que está buscando: una forma de abarcar el espacio nulo. Básicamente, extrae las filas y luego transpone los resultados para que obtenga una matriz con vectores propios como columnas.

+0

Muchas gracias por tomarse el tiempo para responder y ayudarme. Su respuesta fue muy útil para mí, pero acepté la respuesta de Bashworks porque finalmente me llevó a la solución. Sin embargo, la única razón por la que puedo entender la solución es tu respuesta. –

+0

No se preocupe, pensé que su problema era otra cosa. – Wok

2

Su método es casi correcta. El problema es que la forma de s devuelta por la función scipy.linalg.svd es (K,) donde K = min (M, N). Por lo tanto, en su ejemplo, s solo tiene dos entradas (los valores singulares de los primeros dos vectores singulares). La siguiente corrección a su función nula debería permitirle trabajar para cualquier matriz de tamaño.

import scipy 
import numpy as np 
from scipy import linalg, matrix 
def null(A, eps=1e-12): 
... u, s, vh = scipy.linalg.svd(A) 
... padding = max(0,np.shape(A)[1]-np.shape(s)[0]) 
... null_mask = np.concatenate(((s <= eps), np.ones((padding,),dtype=bool)),axis=0) 
... null_space = scipy.compress(null_mask, vh, axis=0) 
... return scipy.transpose(null_space) 
A = matrix([[2,3,5],[-4,2,3]]) 
print A*null(A) 
>>>[[ 4.44089210e-16] 
>>> [ 6.66133815e-16]] 
A = matrix([[1,0,1,0],[0,1,0,0],[0,0,0,0],[0,0,0,0]]) 
print null(A) 
>>>[[ 0.   -0.70710678] 
>>> [ 0.   0.  ] 
>>> [ 0.   0.70710678] 
>>> [ 1.   0.  ]] 
print A*null(A) 
>>>[[ 0. 0.] 
>>> [ 0. 0.] 
>>> [ 0. 0.] 
>>> [ 0. 0.]] 
+1

He estado usando este código en mi trabajo y noté un problema. Un valor de eps de 1e-15 parece ser demasiado pequeño. Notablemente, considere la matriz A = np.ones (13,2). Este código informará que esta matriz tiene un rango 0 de espacio nulo. Esto se debe a que la función scipy.linalg.svd informa que el segundo valor singular está por encima de 1e-15. No sé mucho sobre los algoritmos detrás de esta función, sin embargo, sugiero usar eps = 1e-12 (y quizás menor para matrices muy grandes) a menos que alguien con más conocimiento pueda entrar (en infinita precisión, el segundo valor singular debería ser 0). –

11

Sympy hace esto sencillo.

>>> from sympy import Matrix 
>>> A = [[2, 3, 5], [-4, 2, 3], [0, 0, 0]] 
>>> A = Matrix(A) 
>>> A * A.nullspace()[0] 
Matrix([ 
[0], 
[0], 
[0]]) 
>>> A.nullspace() 
[Matrix([ 
[-1/16], 
[-13/8], 
[ 1]])] 
2

Un método más rápido pero menos estable numéricamente es utilizar a rank-revealing QR decomposition, como scipy.linalg.qr con pivoting=True:

import numpy as np 
from scipy.linalg import qr 

def qr_null(A, tol=None): 
    Q, R, P = qr(A.T, mode='full', pivoting=True) 
    tol = np.finfo(R.dtype).eps if tol is None else tol 
    rnk = min(A.shape) - np.abs(np.diag(R))[::-1].searchsorted(tol) 
    return Q[:, rnk:].conj() 

Por ejemplo:

A = np.array([[ 2, 3, 5], 
       [-4, 2, 3], 
       [ 0, 0, 0]]) 
Z = qr_null(A) 

print(A.dot(Z)) 
#[[ 4.44089210e-16] 
# [ 6.66133815e-16] 
# [ 0.00000000e+00]] 
Cuestiones relacionadas