Skip to content

Commit ffef3b4

Browse files
committed
wip
1 parent e50cda6 commit ffef3b4

File tree

1 file changed

+126
-1
lines changed

1 file changed

+126
-1
lines changed

src/flint/types/fmpz_mat.pyx

Lines changed: 126 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,130 @@ 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+
print(fmpz_mat_nrows(LU.val), fmpz_mat_ncols(LU.val))
608+
d = fmpz.__new__(fmpz)
609+
rank = fmpz_mat_fflu(LU.val, d.val, perm, self.val, 0)
610+
print(fmpz_mat_nrows(LU.val), fmpz_mat_ncols(LU.val))
611+
perm_int = []
612+
for i in range(r):
613+
perm_int.append(perm[i])
614+
finally:
615+
libc.stdlib.free(perm)
616+
617+
return LU, d, perm_int, rank
618+
619+
def fflu(self):
620+
"""
621+
Fraction-free LU decomposition of *self*.
622+
623+
Returns a tuple (*P*, *L*, *D*, *U*) representing the the fraction-free
624+
LU decomposition of a matrix *A* as
625+
626+
P*A = L*inv(D)*U
627+
628+
where *P* is a permutation matrix, *L* is lower triangular, *D* is
629+
diagonal and *U* is upper triangular.
630+
631+
>>> A = fmpz_mat([[1, 2], [3, 4]])
632+
>>> P, L, D, U = A.fflu()
633+
>>> P
634+
[1, 0]
635+
[0, 1]
636+
>>> L
637+
[1, 0]
638+
[3, -2]
639+
>>> D
640+
[1, 0]
641+
[0, -2]
642+
>>> U
643+
[1, 2]
644+
[0, -2]
645+
>>> P*A == L*D.inv()*U
646+
True
647+
648+
This method works for matrices of any shape and rank.
649+
650+
"""
651+
cdef slong r, c
652+
cdef slong i, j, k, l
653+
cdef fmpz di
654+
cdef fmpz_mat P, L, U, LU, D
655+
r = fmpz_mat_nrows(self.val)
656+
c = fmpz_mat_ncols(self.val)
657+
658+
U, d, perm, rank = self._fflu()
659+
660+
P = fmpz_mat(r, r)
661+
for i, pi in enumerate(perm):
662+
fmpz_set_si(fmpz_mat_entry(P.val, i, pi), 1)
663+
664+
L = fmpz_mat(r, r)
665+
666+
i = j = k = 0
667+
while i < r and j < c:
668+
if not fmpz_is_zero(fmpz_mat_entry(U.val, i, j)):
669+
fmpz_set(fmpz_mat_entry(L.val, i, k), fmpz_mat_entry(U.val, i, j))
670+
for l in range(i + 1, r):
671+
fmpz_set(fmpz_mat_entry(L.val, l, k), fmpz_mat_entry(U.val, l, j))
672+
fmpz_set_si(fmpz_mat_entry(U.val, l, j), 0)
673+
i += 1
674+
k += 1
675+
j += 1
676+
677+
for k in range(k, r):
678+
fmpz_set_si(fmpz_mat_entry(L.val, k, k), 1)
679+
680+
D = fmpz_mat(r, r)
681+
682+
if r >= 1:
683+
fmpz_set(fmpz_mat_entry(D.val, 0, 0), fmpz_mat_entry(L.val, 0, 0))
684+
di = fmpz(1)
685+
for i in range(1, r):
686+
fmpz_mul(di.val, fmpz_mat_entry(L.val, i-1, i-1), fmpz_mat_entry(L.val, i, i))
687+
fmpz_set(fmpz_mat_entry(D.val, i, i), di.val)
688+
689+
return P, L, D, U
690+
566691
def rref(self, inplace=False):
567692
"""
568693
Computes the reduced row echelon form (rref) of *self*,

0 commit comments

Comments
 (0)