import numpy as np
ar2f = np.asfortranarray
import nlsic.fpylapack as la
# print fpylapack.__doc__
[docs]def isar(x):
return isinstance(x, np.ndarray)
[docs]class lapack_ssg(Exception):
pass
[docs]class QR:
"""QR class for storing and dealing with QR decomposition by lapack.
Input matrix a and right hand side b are modified by QR() and solve()
respectivly if they are in fortran storage else a copy is made.
If you need just to solve a full column rank, overdetermined
linear system a*x=b and don't need Q, R, rank and so on, use
ls_solve(a, b) instead of this class.
ls_solve() based on dgels lapack routine is slightly quiker for this task"""
def __init__(self, a, rcond=np.finfo(float).eps*100):
self.nrow, self.ncol = a.shape
# make factorization
# NB: q and r are permuted (permutation is given by pivot)
self.qr, self.pivot, self.tau = la.dgeqp3(a)
self.pivot -= 1
d = np.diag(self.qr)
self.rank = np.sum(np.abs(d) >= np.abs(d[0])*rcond)
# packed upper triangular
self.rp = ar2f(self.qr.T[np.tri(self.ncol, self.nrow, dtype=np.bool)])
[docs] def Q(self):
"""get Q matrix from QR form returned by lapack"""
try:
q = self.q
except AttributeError:
self.Qc()
q = self.qc[:, :self.ncol].copy("f")
self.q = q
return q
[docs] def Qc(self):
"""get _complete_ (m x m) Q matrix from QR form returned by lapack"""
try:
qc = self.qc
except AttributeError:
#qc = self.qr.copy("f")
#qc = np.resize(qc, (self.nrow, self.nrow))
##qc=la.dormqr(self.qr, self.tau, np.mat(np.eye(self.nrow)).T)
#qc = la.dorgqr(qc, self.tau)
qc=self.qy(np.eye(self.nrow)).copy("f")
self.qc = qc
return qc
[docs] def R(self):
"""get R matrix from QR form returned by lapack"""
try:
r = self.r
except AttributeError:
n = self.ncol
r = np.triu(self.qr[:n, ])
self.r = r
return r
[docs] def solve(self, b=None):
if b is None:
b = np.mat(np.eye(self.nrow)).T
s = la.dtrtrs(self.qr, la.dormqr(
self.qr, self.tau, b, trans='T'))[:self.ncol, :]
x = np.empty(s.shape, dtype=np.float)
x[self.pivot, :] = s
return x
[docs] def qy(self, y):
"""multiply Q*y (y is modified in place if F-contiguous"""
return la.dormqr(self.qr, self.tau, y, trans='N')
[docs] def qty(self, y):
"""multiply Qt*y"""
return la.dormqr(self.qr, self.tau, y, trans='T')[:self.ncol]
[docs] def Null(self):
"""Claculate basis (column vectors) of Null space of a.T (nat)
such that a.T*nat=0. rank(nat)=nrow(a)-rank(a)
"""
try:
return self.null
except AttributeError:
self.null = np.matrix(self.Qc()[:, self.rank:])
return self.null
[docs]def ls_solve(a, b):
"""
Wraper for lapack dgels routine.
Solve over- or under-determined linear system a*x=b by QR or LR decomposition
return x which has dimensions nrow(x)==ncol(a) and ncol(x)==ncol(b).
Thus, multiple right hand sides in b are allowed.
If a has been already QR factorized by QR(a), you can use qra.solve(b)
where qra is a QR instance.
If the system is under-determined, the least norm solution is returned.
If a and b are F-contiguous they are modified "in place".
You may want to protect them by copying before call.
If a and b are _not_ F-contiguous they are copied inside the call to dgels
(a warning is printed on stderr if -DF2PY_REPORT_ON_ARRAY_COPY was set
during f2py compiling) so the original a and b are _not_ modified"""
x = la.dgels(a, b)
return x[:a.shape[1]]
[docs]def tri_solve(a, b, uplo='U', trans='N'):
"""Solve triangular system a*x=b or at*x=b (trans='N' or 'T')
The matrix a can represent upper or lower triangular system (uplo='U' or 'L')
Other part of rectangular matrix a is not referenced.
b is modified "in place" if it is F-contiguous.
"""
return la.dtrtrs(a, b, uplo=uplo, trans=trans)
if __name__ == "__main__":
from time import time
n = 100
m = 2*n
decimal = np.finfo(float).precision-1-int(np.log10(n))
a = np.ones((m, n), order="F")
a *= np.random.rand(m, n)
a = np.asmatrix(a)
x_true = ar2f(np.asmatrix(np.random.rand(n, 1)))
b = ar2f(a*x_true)
t = time()
x = ls_solve(a.copy('F'), b.copy('F'))
print("time for ls_solve()=", time()-t, "s")
np.testing.assert_array_almost_equal(x, x_true, decimal)
print("n=", n, "; max err=", np.max(np.abs(x-x_true)))
# test if the result is the same
t = time()
x = QR(a.copy('F')).solve(b.copy('F'))
print("time for QR().solve()=", time()-t, "s")
print("n=", n, "; max err=", np.max(np.abs(x-x_true)))
# print "x,x_true=", x, x_true
np.testing.assert_array_almost_equal(x, x_true, decimal)
t = time()
x = QR(a.copy('F'))
print("time for QR()=", time()-t, "s")
qa = QR(a.copy('F'))
t = time()
x = qa.solve(b)
print("time for qa.solve()=", time()-t, "s")
# test Null space
t = time()
na = qa.Null()
print("time for Null()=", time()-t, "s")
np.testing.assert_array_almost_equal(
a.T*qa.Null(), np.zeros((qa.ncol, qa.nrow-qa.rank)), decimal)
np.testing.assert_array_almost_equal(QR(na).rank, qa.nrow-qa.rank, decimal)