web-dev-qa-db-fra.com

Python (NumPy, SciPy), recherche de l'espace nul d'une matrice

J'essaie de trouver l'espace nul (espace de solution de Ax = 0) d'une matrice donnée. J'ai trouvé deux exemples, mais je n'arrive pas à faire travailler l'un ou l'autre. De plus, je ne peux pas comprendre ce qu'ils font pour y arriver, donc je ne peux pas déboguer. J'espère que quelqu'un pourrait me guider à travers cela.

Les pages de documentation ( numpy.linalg.svd et numpy.compress ) sont opaques pour moi. J'ai appris à le faire en créant la matrice C = [A|0], recherche la forme d'échelon de ligne réduite et résolution des variables par ligne. Je n'arrive pas à suivre comment cela se fait dans ces exemples.

Merci pour toute aide!

Voici mon exemple de matrice, qui est le même que le exemple wikipedia :

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

Méthode ( trouvée ici , et ici ):

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)

Quand je l'essaye, je récupère une matrice vide:

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)
>>> 
29
Nona Urbiz

Cela semble bien fonctionner pour moi:

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

Sympy rend cela simple.

>>> 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]])]
17
Idr

Vous obtenez la décomposition SVD de la matrice A. s est un vecteur de valeurs propres. Vous êtes intéressé par des valeurs propres presque nulles (voir $ A * x =\lambda * x $ où $\abs (\ lambda) <\ epsilon $), qui est donné par le vecteur de valeurs logiques null_mask.

Ensuite, vous extrayez de la liste vh les vecteurs propres correspondant aux valeurs propres presque nulles, ce qui est exactement ce que vous recherchez: une façon de couvrir l'espace nul. Fondamentalement, vous extrayez les lignes, puis transposez les résultats afin d'obtenir une matrice avec des vecteurs propres comme colonnes.

9
Wok

Une méthode plus rapide mais moins stable numériquement consiste à utiliser ne décomposition QR révélatrice de rang , comme scipy.linalg.qr avec 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()

Par exemple:

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]]
3
ali_m

Votre méthode est presque correcte. Le problème est que la forme de s retournée par la fonction scipy.linalg.svd est (K,) où K = min (M, N). Ainsi, dans votre exemple, s n'a que deux entrées (les valeurs singulières des deux premiers vecteurs singuliers). La correction suivante de votre fonction null devrait lui permettre de fonctionner pour n'importe quelle matrice de taille.

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.]]
2
Thomas Wentworth

Depuis l'année dernière (2017), scipy dispose désormais d'un null_space méthode dans le scipy.linalg module ( docs ).

implémentation suit la décomposition canonique SVD et est assez petit si vous avez une ancienne version de scipy et devez l'implémenter vous-même (voir ci-dessous). Cependant, si vous êtes à jour, il est là pour vous.

def null_space(A, rcond=None):
    u, s, vh = svd(A, full_matrices=True)
    M, N = u.shape[0], vh.shape[1]
    if rcond is None:
        rcond = numpy.finfo(s.dtype).eps * max(M, N)
    tol = numpy.amax(s) * rcond
    num = numpy.sum(s > tol, dtype=int)
    Q = vh[num:,:].T.conj()
    return Q
2
sputnick