Skip to content

Commit 78668e7

Browse files
committed
wip
1 parent 7786fc0 commit 78668e7

File tree

1 file changed

+124
-1
lines changed

1 file changed

+124
-1
lines changed

src/flint/types/fmpz_mat.pyx

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ from flint.types.fmpz cimport any_as_fmpz
77
from flint.pyflint cimport global_random_state
88
from flint.types.fmpq cimport any_as_fmpq
99
cimport cython
10+
cimport libc.stdlib
1011

11-
from flint.flintlib.functions.fmpz cimport fmpz_set, fmpz_init, fmpz_clear
12+
from flint.flintlib.functions.fmpz cimport fmpz_set, fmpz_init, fmpz_clear, fmpz_set_si, fmpz_mul
1213
from flint.flintlib.functions.fmpz cimport fmpz_is_zero, fmpz_is_pm1
1314
from flint.flintlib.types.fmpz cimport (
1415
fmpz_mat_struct,
@@ -563,6 +564,128 @@ cdef class fmpz_mat(flint_mat):
563564
raise ZeroDivisionError("singular matrix in solve()")
564565
return u
565566

567+
def _fflu(self):
568+
"""
569+
Fraction-free LU decomposition of *self*.
570+
571+
>>> A = fmpz_mat([[1, 2], [3, 4]])
572+
>>> LU, d, perm, rank = A._fflu()
573+
>>> LU
574+
[1, 2]
575+
[3, -2]
576+
>>> d
577+
-2
578+
>>> perm
579+
[0, 1]
580+
>>> rank
581+
2
582+
583+
The matrix *LU* is the LU contains both the lower and upper parts of
584+
the decomposition. The integer *d* is the divisor and is up to a sign
585+
the determinant when *self* is square. The list *perm* is the
586+
permutation of the rows of *self*.
587+
588+
This is the raw output from the underlying FLINT function fmpz_mat_fflu.
589+
The method :meth:`fflu` provides a more understandable representation
590+
of the decomposition.
591+
592+
"""
593+
cdef fmpz d
594+
cdef slong* perm
595+
cdef slong r, c, rank
596+
cdef fmpz_mat LU
597+
r = fmpz_mat_nrows(self.val)
598+
c = fmpz_mat_ncols(self.val)
599+
perm = <slong*>libc.stdlib.malloc(r * sizeof(slong))
600+
if perm is NULL:
601+
raise MemoryError("malloc failed")
602+
try:
603+
for i in range(r):
604+
perm[i] = i
605+
LU = fmpz_mat.__new__(fmpz_mat)
606+
fmpz_mat_init((<fmpz_mat>LU).val, r, c)
607+
d = fmpz.__new__(fmpz)
608+
rank = fmpz_mat_fflu(LU.val, d.val, perm, self.val, 0)
609+
perm_int = []
610+
for i in range(r):
611+
perm_int.append(perm[i])
612+
finally:
613+
libc.stdlib.free(perm)
614+
615+
return LU, d, perm_int, rank
616+
617+
def fflu(self):
618+
"""
619+
Fraction-free LU decomposition of *self*.
620+
621+
Returns a tuple (*P*, *L*, *D*, *U*) representing the the fraction-free
622+
LU decomposition of a matrix *A* as
623+
624+
P*A = L*inv(D)*U
625+
626+
where *P* is a permutation matrix, *L* is lower triangular, *D* is
627+
diagonal and *U* is upper triangular.
628+
629+
>>> A = fmpz_mat([[1, 2], [3, 4]])
630+
>>> P, L, D, U = A.fflu()
631+
>>> P
632+
[1, 0]
633+
[0, 1]
634+
>>> L
635+
[1, 0]
636+
[3, -2]
637+
>>> D
638+
[1, 0]
639+
[0, -2]
640+
>>> U
641+
[1, 2]
642+
[0, -2]
643+
>>> P*A == L*D.inv()*U
644+
True
645+
646+
This method works for matrices of any shape and rank.
647+
648+
"""
649+
cdef slong r, c
650+
cdef slong i, j, k, l
651+
cdef fmpz di
652+
cdef fmpz_mat P, L, U, D
653+
r = fmpz_mat_nrows(self.val)
654+
c = fmpz_mat_ncols(self.val)
655+
656+
U, _d, perm, _rank = self._fflu()
657+
658+
P = fmpz_mat(r, r)
659+
for i, pi in enumerate(perm):
660+
fmpz_set_si(fmpz_mat_entry(P.val, i, pi), 1)
661+
662+
L = fmpz_mat(r, r)
663+
664+
i = j = k = 0
665+
while i < r and j < c:
666+
if not fmpz_is_zero(fmpz_mat_entry(U.val, i, j)):
667+
fmpz_set(fmpz_mat_entry(L.val, i, k), fmpz_mat_entry(U.val, i, j))
668+
for l in range(i + 1, r):
669+
fmpz_set(fmpz_mat_entry(L.val, l, k), fmpz_mat_entry(U.val, l, j))
670+
fmpz_set_si(fmpz_mat_entry(U.val, l, j), 0)
671+
i += 1
672+
k += 1
673+
j += 1
674+
675+
for k in range(k, r):
676+
fmpz_set_si(fmpz_mat_entry(L.val, k, k), 1)
677+
678+
D = fmpz_mat(r, r)
679+
680+
if r >= 1:
681+
fmpz_set(fmpz_mat_entry(D.val, 0, 0), fmpz_mat_entry(L.val, 0, 0))
682+
di = fmpz(1)
683+
for i in range(1, r):
684+
fmpz_mul(di.val, fmpz_mat_entry(L.val, i-1, i-1), fmpz_mat_entry(L.val, i, i))
685+
fmpz_set(fmpz_mat_entry(D.val, i, i), di.val)
686+
687+
return P, L, D, U
688+
566689
def rref(self, inplace=False):
567690
"""
568691
Computes the reduced row echelon form (rref) of *self*,

0 commit comments

Comments
 (0)