Random solutions of indefinite linear systems

Consider an indefinite linear system of equations Ax=b

.

I would like to find a set of vectors x_1, ..., x_n

such that they decide everything Ax=b

, and they are as different from each other as possible.

The second part is actually less important; I would be happy with an algorithm that returns a random solution Ax=b

every time I call it.

I know that scipy.sparse.linalg.lsqr

and numpy.linalg.lstsq

return a sparse solution (in terms of least squares) to an underdetermined linear system Ax=b

, but I am not interested in the properties of the solution; I just want any solution Ax=b

if I can create many different solutions.

In fact, scipy.sparse.linalg.lsqr

and numpy.linalg.lstsq

must follow an iterative process that moves from solution to solution until it finds a solution that appears to be least squares. Well, is there a python module that allows me to switch between solutions without any specific purpose and return them?

+3


source to share


2 answers


For underdetermined system A ยท x = b , you can calculate the empty space of your matrix coefficients A . The empty space, Z , is the set of basis vectors enclosing the subspace A such that A ยท Z = 0 . In other words, the column Z are vectors orthogonal to all the rows in A . This means that for any solution x ' up to A x = b , then x' + Z c must also be a solution for any arbitrary vector c .

So, if you want to try random solutions for A x = b , you can do the following:

For example:



import numpy as np
from scipy.linalg import qr


def qr_null(A, tol=None):
    """Computes the null space of A using a rank-revealing QR decomposition"""
    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()


# An underdetermined system with nullity 2
A = np.array([[1, 4, 9, 6, 9, 2, 7],
              [6, 3, 8, 5, 2, 7, 6],
              [7, 4, 5, 7, 6, 3, 2],
              [5, 2, 7, 4, 7, 5, 4],
              [9, 3, 8, 6, 7, 3, 1]])
b = np.array([0, 4, 1, 3, 2])

# Find an initial solution using `np.linalg.lstsq`
x_lstsq = np.linalg.lstsq(A, b)[0]

# Compute the null space of `A`
Z = qr_null(A)
nullity = Z.shape[1]

# Sample some random solutions
for _ in range(5):
    x_rand = x_lstsq + Z.dot(np.random.rand(nullity))
    # If `x_rand` is a solution then `||Aยทx_rand - b||` should be very small
    print(np.linalg.norm(A.dot(x_rand) - b))

      

Output example:

3.33066907388e-15
3.58036167305e-15
4.63775652864e-15
4.67877015036e-15
4.31132637123e-15

      

The space of possible c vectors is infinite - you will need to make choices about how you want to try them.

+1


source


Here is the code accompanying my comment. It uses a module rank_nullspace.py

from the Scipy Cookbook .

import numpy as np
from numpy.linalg import lstsq

from rank_nullspace import nullspace
# rank_nullspace from
# http://scipy-cookbook.readthedocs.io/items/RankNullspace.html


def randsol(A, b, num=1, check=False):
    xLS, *_ = lstsq(A, b)

    colsOfNullspace = nullspace(A)
    nullrank = colsOfNullspace.shape[1]
    if check:
        assert(np.allclose(np.dot(A, xLS), b))
        assert(np.allclose(np.dot(A, xLS + np.dot(colsOfNullspace,
                                                  np.random.randn(nullrank))),
                           b))

    sols = xLS[:, np.newaxis] + np.dot(colsOfNullspace,
                                       np.random.randn(nullrank, num))
    return sols


A = np.random.randn(2, 10)
b = np.random.randn(2)
x = randsol(A, b, num=50, check=True)
assert(np.allclose(np.dot(A, x), b[:, np.newaxis]))

      



With a bunch of solutions in there, x

you can choose ones that are "different" from each other, however you define "different".

+1


source







All Articles