Repository: sagemath/sage

Issue:
Extend MAX_MODULUS for `matrix_modn_dense_double.pyx` from 23 bits to 27 bits
### Is there an existing issue for this?

- [X] I have searched the existing issues for a bug report that matches the one I want to file, without success.

### Problem Description

In `matrix_modn_dense_double.pyx` (l.40), `MAX_MODULUS` is set to 2^23 which is mentioned as the limit for LinBox `Modular<double>` . 

In Givaro's documentation `/src/kernel/ring/modular-implem.h` (l.136), `maxCardinality()` can go up to 94906266 = floor(2^26*sqrt(2) + 1/2) which is the actual limit used by LinBox `Modular<double>`.

I was wondering if we can match `MAX_MODULUS` with this value and if there would be any subsequent trouble.

### Proposed Solution

In `matrix_modn_dense_double.pyx` (l.40), change the value of `MAX_MODULUS` to 94906266.

In `matrix_space.py` (l.169 and l.620), replace 8388608 (=2^23)  by 94906266.



### Alternatives Considered

?

### Additional Information

Timings were made using the following commands:

```
sage: M = MatrixSpace(GF(4194319), 1000, 1000)
sage: A = M.random_element()
sage: B = M.random_element()
sage: %time C = A * B
sage: %time R = A.rref()
```

Primes used for the matrix space:
k -> next_prime(2**k), and MaxCardinality i.e. = floor(2^26*sqrt(2) + 1/2)
```
MAX_MODULUS     2^23       |     2^24       |      2^25       |      2^26       |   94906266
nbits     |  mul      ref  |  mul     ref   | mul       ref   |  mul      ref   |  mul     ref    
23        | 210ms    168ms | 261ms   166ms  | 205ms    164ms  | 218ms    168ms  | 213ms   163ms        
24        | 1min59s  46.2s | 176ms   73.7ms | 178ms    79.3ms | 183ms    76.5ms | 173ms   74ms        
25        | 2min8s   44.2s | 2min4s  43.9s  | 213ms    92ms   | 216ms    87.8ms | 217ms   83.9ms    
26        | 2min5s   45.6s | 2min2s  43.9s  | 2min5s   43.9s  | 373ms    154ms  | 376ms   152ms
27        | 2min6s   45.1s | 2min    43.7s  | 2min5s   44.7s  | 2min5s   44.4s  | 1.61s   622ms
28        | 2min13s  46.9s | 2min    43.6s  | 2min2s   44.8s  | 2min2s   44.2s  | 2min5s  44.8s
```


Discussion:
The timings suggest this change would be interesting indeed (this only improves timings for a small range of bitsizes, but the improvement is significant and the changes to be done seem quite minor).

@marizee These timings suggest you have compiled sage with the suggested changes. Did you have the opportunity to launch the tests with this modified values?

@ClementPernet could you please confirm that this maxCardinality from Givaro is what is used as the limit for the Modular<double> matrices in fflas-ffpack? (and, for the sake of understanding the previous choice: was 23 bits a limit imposed some years ago in the LinBox ecosystem?)
Yes.

I have been careful to compile Sage and run tests at least for `src/sage/matrix` between each value of `MAX_MODULUS`.
This is how I figured out that `matrix_space.py` also needs modifications.

For `maxCardinality()`, I have run all Sage tests and they all passed. 
Great, this is definitely an important change to be done. At the time where we wrote the ```2**23```, Givaro's maxCardinality had not been clarified.
It was set to ```2**23``` as the computational bit-speed usually start decreasing nearby this bitsize (in the context of a residue number system where one is free to choose the bitsize). However this was a mistake, since as a user, what really matters is the best speed for a given bitsize, hence one need to compare to alternative implementation (as you did).
I am surprised to see that even at 28 bits, fflas is still competitive, I would have never expected so.
For matrix product in sage (c=AB) this choice is safe.
I still have a 1% uncertainty concerining the situation where one want to use other routine in fflas, in particular a matrix product combined with an accumalation  (fgemm, with beta!=0). I need to look at this point closer to answer correctly.

Thanks for your input. I have just checked that sage does not use FFLAS' fgemm with `beta!=0`, at least directly. It may use it indirectly via calls to other LinBox / fflas-ffpack methods, but then these methods should be the ones catching potential issues I suppose; also sage tests did cover these other methods and have not revealed any problem. (Also I've just tried running fflas-ffpack's fgemm benchmark for Modular<double> with `beta=1` and did not see any noticeable change in behaviour or performance either well below maxCardinality or very close to it.)

So, my understanding from your message is: we could do this change right now, but we should also keep this point in mind and perform suitable verifications if one day we import more functionalities from the LinBox ecosystem into SageMath.

Resolving patch:
diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py
--- a/src/sage/matrix/matrix_space.py
+++ b/src/sage/matrix/matrix_space.py
@@ -166,7 +166,7 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation):
         sage: get_matrix_class(Zmod(2**30), 2, 2, False, 'linbox-double')
         Traceback (most recent call last):
         ...
-        ValueError: 'linbox-double' matrices can only deal with order < 8388608
+        ValueError: 'linbox-double' matrices can only deal with order < 94906266
 
         sage: type(matrix(SR, 2, 2, 0))
         <class 'sage.matrix.matrix_symbolic_dense.Matrix_symbolic_dense'>
@@ -617,7 +617,8 @@ def __init__(self, base_ring, nrows, ncols, sparse, implementation):
 
            - ``linbox-float`` - for integer mod rings up to `2^8 = 256`
 
-           - ``linbox-double`` - for integer mod rings up to `2^23 = 8388608`
+           - ``linbox-double`` - for integer mod rings up to
+             `floor(2^26*sqrt(2) + 1/2) = 94906266`
 
            - ``numpy`` - for real and complex floating point numbers
 


Test patch: