Bill Allombert on Sat, 23 May 2009 17:53:06 +0200

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: Another problem with matrix inversion

On Thu, May 21, 2009 at 04:43:46AM +0200, Lorenz Minder wrote:
> Hi,
> BA:
> > Sorry for the rushed our reply, but maybe you would prefer it to 
> > a long delayed one.
> I absolutely do, yes.  Thanks for taking the time.

But I am afraid this lead to misunderstanding. Sorry about that.

> I'll fix the trivial stuff you mentionned (and also a bug I spotted),
> but there are more fundamental issues that I think need discussion.
> > You also need to add a test-suite, and the documentation of the new public
> > function.
> I'll look in the test suite, not sure how this works.  (Documentation
> is noted, let's talk about this later.)

Test-suite is merely a matter of adding a file src/test/in/foo or
expanding an existing file that is not used by 'make bench'.

> > I am a bit concerned that your patch is large, fixing the interface issue
> > would make it larger yet,

First, when I said the patch was large, I really meant that it added a lot of
code to PARI, not so much that it made a lot of change.

> I strongly suggest to go back to the way I did it originally, namely
> simply replace FpM_gauss() and Flm_gauss().  Let me quote from the

I have an alternative:
1) We add function FpM_invsafe/Flm_invsafe that would do that same for matrix
inversion that Fp_invsafe do for integers. Maybe even FpM_gausssafe if needed.
This is a minimal change, that use a well-defined interface.
I can do it if you want.

2) We add a function that take a factored modulus and use chinese remainder and
Hensel lifting to use FpM_invsafe to inverse a matrix. Hensel lifting alleviate
the need to change FpM_gauss to look for an invertible pivot. This is similar
to how gen_Shanks_sqrtn works.

3) We add a function that take an unfactored modulus, call the function above
as if the modulus is prime, and if not, refine the factorisation and iterate.

> libpari manual, the part that talks about Fp* and Fq* functions:
>   7.2 Modular arithmetic. 
>   These routines implement univariate polynomial arithmetic and linear
>   algebra over finite fields, in fact over finite rings of the form
>   (Z/pZ)[X]/(T), where p is not necessarily prime and T \in (Z/pZ)[X]
>   is possibly reducible; and finite extensions thereof. All this can be
>   emulated with t_INTMOD and t_POLMOD coefficients and using generic
>   routines, at a considerable loss of efficiency.
> As far as I can see, this states unambiguously that p does not have to
> be prime.  Don't you agree?

I do not. I designed this interface and this says that the Fp operations 
perform as if the ring is a finite field. This means they do not go out 
of their way to handle non-fields.  The whole point of the Fp operations being
to get rid of genericity, which is quite costly.
In practice inside PARI this is used for non-field only for prime power
to handle operation over Z/p^mZ (e.g. Hensel lifting).

> I suggest we start thinking of this patch as what it is, a bug fix.

it is not a "bug fix": the PARI documentation is quite clear on that:
Vectors, matrices, linear algebra and sets:

   Since PARI does not have a strong typing system, scalars live in unspecified
commutative  base  rings.   It is very difficult to write robust linear algebra
routines  in  such  a general setting.   We thus assume that the base ring is a
domain and work over its field of fractions.  If the base ring is not a domain,
one  gets  an error as soon as a non-zero pivot turns out to be non-invertible.
Some  functions,   e.g. mathnf or mathnfmod,  specifically assume that the base
ring is Z.
This describes exactly the situation here, so it is not a bug.

Now I agree that, as a matter of "quality of implementation", it would be
nice if PARI was able to inverse all invertible matrices. But this is not a

> >and does not handle the general matrix inversion
> > problem. 
> That was never the intention, and there are plenty of other patches
> that don't do that either.  Of course, if would be great if PARI would
> more generically invert matrices, but that's an orthogonal issue,
> really.  (Unless the generic algorithm would be as fast as what is
> there now, but frankly I don't buy that without having seen it.  For
> the record, the matadjoint(,1) thing is so slow it's useless for me
> currently.)

OK, I have made some test and I agree that matadjoint is very slow,
even compared to plain RgM_inv without the is_FpM otpimisation.

> Is the patch large? Possibly, but unless/until someone comes up with a
> significantly shorter algorithm that performs equally well, why does
> it matter?

There is always a cost (in storage/ memory/ testing/ maintainance/
consistentcy/ etc.) associate to extra code in PARI. This has to be
balanced with use case.

> > It would be nice if the whole strategy was abstracted to be reused in
> > similar
> > situation.
> FqM_gauss() has the analogous bug of course, and it may be possible to
> reuse the same kind of logic, and possibly share some code if p is a
> prime but the polynomial reducible.  But if p=9, we get things like
> (3x + 1)^3 = 1, and I'm pretty sure that will screw us, although I
> admit not having thought about this carefully.

Agreed. However, factoring polynomials over a field is much easier than
factoring integers, so this trink is not very interesting for polynomials.

> My take on it is that it is not hard to make it more abstract later
> if it proves useful, but I think there is really not much of a point
> doing it just now.
> > Do you think there is really a need for 'ZlM' ?  In that range, factoring
> > the modulus is trivial, and that would reduce the size of the patch 
> > somewhat.
> For Flm_gauss() the problem could indeed be solved differently, but
> I'm not sure it's so much easier, especially since the case of p having
> square factors needs to be considered, and the elimination routines
> fixed accordingly in any case.

This is why I suggest to use Hensel lifting.


> +    {
> +      GEN piv = remii(gcoeff(a,k,i), p);
> +      if (signe(piv)) {
> +        GEN res;
> +     if(!invmod(piv, p, &res)) {
> +       if(mod_is_prime) {
> +         /* Trigger error */
> +         Fp_inv(piv, p);
> +       }

Please use Fp_invsafe() (and implement a function Fl_invsafe for the Fl case).

Could you avoid the code duplication between Z_find_coprime_factors and
find_coprime_factors ? I do not think this is performance critical enough
to warrant it.