@@ -7,8 +7,9 @@ from flint.types.fmpz cimport any_as_fmpz
77from flint.pyflint cimport global_random_state
88from flint.types.fmpq cimport any_as_fmpq
99cimport 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
1213from flint.flintlib.functions.fmpz cimport fmpz_is_zero, fmpz_is_pm1
1314from 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