passagemath-meataxe 10.6.46__cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. passagemath_meataxe/__init__.py +3 -0
  2. passagemath_meataxe-10.6.46.dist-info/METADATA +97 -0
  3. passagemath_meataxe-10.6.46.dist-info/METADATA.bak +98 -0
  4. passagemath_meataxe-10.6.46.dist-info/RECORD +85 -0
  5. passagemath_meataxe-10.6.46.dist-info/WHEEL +7 -0
  6. passagemath_meataxe-10.6.46.dist-info/top_level.txt +3 -0
  7. passagemath_meataxe.libs/libmtx-dd5e7e28.so.0.0.0 +0 -0
  8. sage/all__sagemath_meataxe.py +2 -0
  9. sage/libs/all__sagemath_meataxe.py +1 -0
  10. sage/libs/meataxe.cpython-310-aarch64-linux-gnu.so +0 -0
  11. sage/libs/meataxe.pxd +156 -0
  12. sage/libs/meataxe.pyx +95 -0
  13. sage/matrix/all__sagemath_meataxe.py +1 -0
  14. sage/matrix/matrix_gfpn_dense.cpython-310-aarch64-linux-gnu.so +0 -0
  15. sage/matrix/matrix_gfpn_dense.pxd +37 -0
  16. sage/matrix/matrix_gfpn_dense.pyx +1960 -0
  17. sage_wheels/share/meataxe/p002.zzz +0 -0
  18. sage_wheels/share/meataxe/p003.zzz +0 -0
  19. sage_wheels/share/meataxe/p004.zzz +0 -0
  20. sage_wheels/share/meataxe/p005.zzz +0 -0
  21. sage_wheels/share/meataxe/p007.zzz +0 -0
  22. sage_wheels/share/meataxe/p008.zzz +0 -0
  23. sage_wheels/share/meataxe/p009.zzz +0 -0
  24. sage_wheels/share/meataxe/p011.zzz +0 -0
  25. sage_wheels/share/meataxe/p013.zzz +0 -0
  26. sage_wheels/share/meataxe/p016.zzz +0 -0
  27. sage_wheels/share/meataxe/p017.zzz +0 -0
  28. sage_wheels/share/meataxe/p019.zzz +0 -0
  29. sage_wheels/share/meataxe/p023.zzz +0 -0
  30. sage_wheels/share/meataxe/p025.zzz +0 -0
  31. sage_wheels/share/meataxe/p027.zzz +0 -0
  32. sage_wheels/share/meataxe/p029.zzz +0 -0
  33. sage_wheels/share/meataxe/p031.zzz +0 -0
  34. sage_wheels/share/meataxe/p032.zzz +0 -0
  35. sage_wheels/share/meataxe/p037.zzz +0 -0
  36. sage_wheels/share/meataxe/p041.zzz +0 -0
  37. sage_wheels/share/meataxe/p043.zzz +0 -0
  38. sage_wheels/share/meataxe/p047.zzz +0 -0
  39. sage_wheels/share/meataxe/p049.zzz +0 -0
  40. sage_wheels/share/meataxe/p053.zzz +0 -0
  41. sage_wheels/share/meataxe/p059.zzz +0 -0
  42. sage_wheels/share/meataxe/p061.zzz +0 -0
  43. sage_wheels/share/meataxe/p064.zzz +0 -0
  44. sage_wheels/share/meataxe/p067.zzz +0 -0
  45. sage_wheels/share/meataxe/p071.zzz +0 -0
  46. sage_wheels/share/meataxe/p073.zzz +0 -0
  47. sage_wheels/share/meataxe/p079.zzz +0 -0
  48. sage_wheels/share/meataxe/p081.zzz +0 -0
  49. sage_wheels/share/meataxe/p083.zzz +0 -0
  50. sage_wheels/share/meataxe/p089.zzz +0 -0
  51. sage_wheels/share/meataxe/p097.zzz +0 -0
  52. sage_wheels/share/meataxe/p101.zzz +0 -0
  53. sage_wheels/share/meataxe/p103.zzz +0 -0
  54. sage_wheels/share/meataxe/p107.zzz +0 -0
  55. sage_wheels/share/meataxe/p109.zzz +0 -0
  56. sage_wheels/share/meataxe/p113.zzz +0 -0
  57. sage_wheels/share/meataxe/p121.zzz +0 -0
  58. sage_wheels/share/meataxe/p125.zzz +0 -0
  59. sage_wheels/share/meataxe/p127.zzz +0 -0
  60. sage_wheels/share/meataxe/p128.zzz +0 -0
  61. sage_wheels/share/meataxe/p131.zzz +0 -0
  62. sage_wheels/share/meataxe/p137.zzz +0 -0
  63. sage_wheels/share/meataxe/p139.zzz +0 -0
  64. sage_wheels/share/meataxe/p149.zzz +0 -0
  65. sage_wheels/share/meataxe/p151.zzz +0 -0
  66. sage_wheels/share/meataxe/p157.zzz +0 -0
  67. sage_wheels/share/meataxe/p163.zzz +0 -0
  68. sage_wheels/share/meataxe/p167.zzz +0 -0
  69. sage_wheels/share/meataxe/p169.zzz +0 -0
  70. sage_wheels/share/meataxe/p173.zzz +0 -0
  71. sage_wheels/share/meataxe/p179.zzz +0 -0
  72. sage_wheels/share/meataxe/p181.zzz +0 -0
  73. sage_wheels/share/meataxe/p191.zzz +0 -0
  74. sage_wheels/share/meataxe/p193.zzz +0 -0
  75. sage_wheels/share/meataxe/p197.zzz +0 -0
  76. sage_wheels/share/meataxe/p199.zzz +0 -0
  77. sage_wheels/share/meataxe/p211.zzz +0 -0
  78. sage_wheels/share/meataxe/p223.zzz +0 -0
  79. sage_wheels/share/meataxe/p227.zzz +0 -0
  80. sage_wheels/share/meataxe/p229.zzz +0 -0
  81. sage_wheels/share/meataxe/p233.zzz +0 -0
  82. sage_wheels/share/meataxe/p239.zzz +0 -0
  83. sage_wheels/share/meataxe/p241.zzz +0 -0
  84. sage_wheels/share/meataxe/p243.zzz +0 -0
  85. sage_wheels/share/meataxe/p251.zzz +0 -0
@@ -0,0 +1,1960 @@
1
+ # distutils: libraries = mtx
2
+ # sage_setup: distribution = sagemath-meataxe
3
+ # sage.doctest: optional - meataxe
4
+
5
+ r"""
6
+ Dense Matrices over `\mathbb F_q`, with `q<255`.
7
+
8
+ This module is a wrapper for the `Aachen C-MeatAxe library <../spkg/meataxe.html>`_,
9
+ improved by an implementation of the Winograd-Strassen multiplication
10
+ algorithm. It provides matrices over the finite field `\mathbb F_q`,
11
+ where `q\le 255`.
12
+
13
+ By default, it is only used when `q` is odd and not prime, because other
14
+ matrix implementations in SageMath perform better for prime fields or in
15
+ characteristic two.
16
+
17
+ .. NOTE::
18
+
19
+ The examples shown here will only work when the `meataxe
20
+ <../spkg/meataxe.html>` package has been installed.
21
+
22
+ AUTHORS:
23
+
24
+ - Simon King (2015-09): initial version
25
+ """
26
+
27
+ # ***************************************************************************
28
+ # Copyright (C) 2015 Simon King <simon.king@uni-jena.de>
29
+ #
30
+ # This program is free software: you can redistribute it and/or modify
31
+ # it under the terms of the GNU General Public License as published by
32
+ # the Free Software Foundation, either version 2 of the License, or
33
+ # (at your option) any later version.
34
+ # https://www.gnu.org/licenses/
35
+ # ***************************************************************************
36
+ from cysignals.memory cimport check_realloc, check_malloc, sig_free
37
+ from cpython.bytes cimport PyBytes_AsString, PyBytes_FromStringAndSize
38
+ from cysignals.signals cimport sig_on, sig_off, sig_check
39
+ cimport cython
40
+
41
+ ####################
42
+ #
43
+ # import sage types
44
+ #
45
+ ####################
46
+
47
+ from sage.cpython.string import FS_ENCODING
48
+ from sage.rings.integer import Integer
49
+ from sage.rings.finite_rings.finite_field_constructor import GF
50
+ from sage.rings.finite_rings.integer_mod import IntegerMod_int
51
+ from sage.matrix.matrix_space import MatrixSpace
52
+ from sage.misc.randstate import current_randstate
53
+ from sage.misc.randstate cimport randstate
54
+ from sage.structure.element cimport Element, Matrix
55
+ from sage.structure.richcmp import rich_to_bool
56
+ from sage.matrix.args cimport MatrixArgs_init
57
+
58
+ from libc.string cimport memset, memcpy
59
+
60
+ cimport sage.matrix.matrix0
61
+
62
+ # The following import is just to ensure that meataxe_init() is called.
63
+ import sage.libs.meataxe
64
+
65
+ ####################
66
+ #
67
+ # auxiliary functions
68
+ #
69
+ ####################
70
+
71
+ # Fast conversion from field to int and int to field
72
+ cdef class FieldConverter_class:
73
+ """
74
+ An auxiliary class, used to convert between meataxe ``FEL`` and
75
+ finite field elements.
76
+
77
+ This class is for Givaro finite fields only. It exists in order to
78
+ have a common interface for elements of prime and non-prime fields;
79
+ see :class:`PrimeFieldConverter_class`.
80
+
81
+ .. NOTE::
82
+
83
+ This class is really meant to be used only from Cython.
84
+ We do make the methods available from Python, but that is
85
+ for testing only.
86
+
87
+ .. WARNING::
88
+
89
+ Before calling the ``fel_to_field`` or ``field_to_fel`` methods,
90
+ one should call the ``FfSetField`` function.
91
+
92
+ EXAMPLES::
93
+
94
+ sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense
95
+ sage: F.<y> = GF(125)
96
+ sage: M = MatrixSpace(F, 2, 2, implementation=Matrix_gfpn_dense).one()
97
+ sage: C = M._converter
98
+ sage: C.fel_to_field(15)
99
+ 3*y
100
+ sage: F.from_integer(15)
101
+ 3*y
102
+ sage: C.field_to_fel(y)
103
+ 5
104
+ sage: y.to_integer()
105
+ 5
106
+ """
107
+ def __init__(self, field):
108
+ """
109
+ INPUT:
110
+
111
+ - ``field`` -- a finite field with Givaro implementation and at most 251
112
+ elements. These assumptions are not tested.
113
+
114
+ EXAMPLES::
115
+
116
+ sage: from sage.matrix.matrix_gfpn_dense import FieldConverter_class
117
+ sage: FieldConverter_class(GF(13^2))
118
+ <sage.matrix.matrix_gfpn_dense.FieldConverter_class object at ...>
119
+ """
120
+ self.field = field._cache.fetch_int
121
+ self.zero_FEL = self.field_to_fel(field.zero())
122
+
123
+ cpdef fel_to_field(self, FEL x) noexcept:
124
+ """
125
+ Fetch a python int into the field.
126
+
127
+ EXAMPLES::
128
+
129
+ sage: from sage.matrix.matrix_gfpn_dense import FieldConverter_class
130
+ sage: F.<y> = GF(125)
131
+ sage: C = FieldConverter_class(F)
132
+ sage: C.fel_to_field(15)
133
+ 3*y
134
+ sage: F.from_integer(15)
135
+ 3*y
136
+ """
137
+ return self.field(FfToInt(x))
138
+
139
+ cpdef FEL field_to_fel(self, x) except 255:
140
+ """
141
+ Represent a field element by a python int.
142
+
143
+ EXAMPLES::
144
+
145
+ sage: from sage.matrix.matrix_gfpn_dense import FieldConverter_class
146
+ sage: F.<y> = GF(125)
147
+ sage: C = FieldConverter_class(F)
148
+ sage: C.field_to_fel(y)
149
+ 5
150
+ sage: y.to_integer()
151
+ 5
152
+
153
+ TESTS:
154
+
155
+ Test invalid input::
156
+
157
+ sage: C.field_to_fel('foo')
158
+ Traceback (most recent call last):
159
+ ...
160
+ AttributeError: 'str' object has no attribute 'to_integer'...
161
+ """
162
+ return FfFromInt(x.to_integer())
163
+
164
+
165
+ cdef class PrimeFieldConverter_class(FieldConverter_class):
166
+ """
167
+ An auxiliary class, used to convert between meataxe ``FEL`` and
168
+ finite field elements.
169
+
170
+ This class is for prime fields only. It exists in order to have a
171
+ common interface for elements of prime and non-prime fields; see
172
+ :class:`FieldConverter_class`.
173
+
174
+ .. NOTE::
175
+
176
+ This class is really meant to be used only from Cython.
177
+ We do make the methods available from Python, but that is
178
+ for testing only.
179
+
180
+ .. WARNING::
181
+
182
+ Before calling the ``fel_to_field`` or ``field_to_fel`` methods,
183
+ one should call the ``FfSetField`` function.
184
+
185
+ EXAMPLES::
186
+
187
+ sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense
188
+ sage: F = GF(5)
189
+ sage: M = MatrixSpace(F, 2, 2, implementation=Matrix_gfpn_dense).one()
190
+ sage: C = M._converter
191
+ sage: C.fel_to_field(2)
192
+ 2
193
+ sage: F(2)
194
+ 2
195
+ sage: C.field_to_fel(F(2))
196
+ 2
197
+ sage: int(F(2))
198
+ 2
199
+ """
200
+ def __init__(self, field):
201
+ """
202
+ INPUT:
203
+
204
+ - ``field`` -- a finite *prime* field with at most 251 elements.
205
+ This assumption is not tested.
206
+
207
+ EXAMPLES::
208
+
209
+ sage: from sage.matrix.matrix_gfpn_dense import PrimeFieldConverter_class
210
+ sage: PrimeFieldConverter_class(GF(251))
211
+ <sage.matrix.matrix_gfpn_dense.PrimeFieldConverter_class object at ...>
212
+ """
213
+ self.field = field
214
+
215
+ cpdef fel_to_field(self, FEL x) noexcept:
216
+ """
217
+ Fetch a python int into the field.
218
+
219
+ EXAMPLES::
220
+
221
+ sage: from sage.matrix.matrix_gfpn_dense import PrimeFieldConverter_class
222
+ sage: F = GF(5)
223
+ sage: C = PrimeFieldConverter_class(F)
224
+ sage: C.fel_to_field(2)
225
+ 2
226
+ """
227
+ return IntegerMod_int(self.field, FfToInt(x))
228
+
229
+ cpdef FEL field_to_fel(self, x) except 255:
230
+ """
231
+ Represent a field element by a python int.
232
+
233
+ EXAMPLES::
234
+
235
+ sage: from sage.matrix.matrix_gfpn_dense import PrimeFieldConverter_class
236
+ sage: F = GF(5)
237
+ sage: C = PrimeFieldConverter_class(F)
238
+ sage: C.field_to_fel(F(2))
239
+ 2
240
+
241
+ TESTS:
242
+
243
+ Test invalid input::
244
+
245
+ sage: C.field_to_fel('foo')
246
+ Traceback (most recent call last):
247
+ ...
248
+ TypeError: an integer is required
249
+ """
250
+ return FfFromInt(x)
251
+
252
+
253
+ cdef dict _converter_cache = {}
254
+ cdef FieldConverter_class FieldConverter(field) noexcept:
255
+ """
256
+ Return a :class:`FieldConverter_class` or :class:`PrimeFieldConverter_class` instance,
257
+ depending whether the field is prime or not.
258
+
259
+ EXAMPLES::
260
+
261
+ sage: MS = MatrixSpace(GF(5^3,'y'),2)
262
+ sage: A = MS.random_element()
263
+ sage: A*2 == A+A # indirect doctest
264
+ True
265
+ sage: A = MS.random_element()
266
+ sage: A*2 == A+A
267
+ True
268
+ """
269
+ try:
270
+ return _converter_cache[field]
271
+ except KeyError:
272
+ if field.is_prime_field():
273
+ return _converter_cache.setdefault(field, PrimeFieldConverter_class(field))
274
+ return _converter_cache.setdefault(field, FieldConverter_class(field))
275
+
276
+ ######################################
277
+ ##
278
+ ## Wrapper for MeatAxe matrices
279
+ ##
280
+ ######################################
281
+
282
+ cdef Matrix_gfpn_dense new_mtx(Matrix_t* mat, Matrix_gfpn_dense template) noexcept:
283
+ """
284
+ Create a new ``Matrix_gfpn_dense`` from a meataxe matrix
285
+
286
+ INPUT:
287
+
288
+ - ``mat`` -- (``Matrix_t*``) a meataxe matrix
289
+
290
+ - ``template`` -- an optional matrix with the same base ring as the
291
+ new matrix being created. This can also be ``None``.
292
+
293
+ ``template`` is only used for efficiency: various objects are simply
294
+ taken from the template instead of being constructed. The condition
295
+ that the base ring is the same is not checked.
296
+ """
297
+ assert mat is not NULL
298
+
299
+ if template is not None:
300
+ B = template._base_ring
301
+ conv = template._converter
302
+ src = template.Data
303
+ if src is not NULL and src.Nor == mat.Nor and src.Noc == mat.Noc:
304
+ # Same dimensions
305
+ MS = template._parent
306
+ else:
307
+ # Different dimensions
308
+ MS = MatrixSpace(B, mat.Nor, mat.Noc, implementation=Matrix_gfpn_dense)
309
+ else:
310
+ B = GF(mat.Field, 'z')
311
+ conv = FieldConverter(B)
312
+ MS = MatrixSpace(B, mat.Nor, mat.Noc, implementation=Matrix_gfpn_dense)
313
+
314
+ ret = <Matrix_gfpn_dense>Matrix_gfpn_dense.__new__(Matrix_gfpn_dense, MS)
315
+ ret.Data = mat
316
+ ret._converter = conv
317
+ return ret
318
+
319
+
320
+ cdef class Matrix_gfpn_dense(Matrix_dense):
321
+ r"""
322
+ Dense matrices over `\mathbb F_q`, `q<255` odd and not prime.
323
+
324
+ NOTE:
325
+
326
+ This class uses a major modification of the Aachen C-MeatAxe
327
+ as backend. In principle, it would also work for prime fields
328
+ and in characteristic two. However, other matrices in Sage,
329
+ relying on linbox, m4ri or m4rie, are more efficient in these
330
+ cases.
331
+
332
+ EXAMPLES::
333
+
334
+ sage: M = MatrixSpace(GF(25,'z'),2,3)([1,2,3,4,5,6])
335
+ sage: print(M)
336
+ [1 2 3]
337
+ [4 0 1]
338
+ sage: type(M)
339
+ <class 'sage.matrix.matrix_gfpn_dense.Matrix_gfpn_dense'>
340
+
341
+ The documentation of the ``__init__`` methods shows further
342
+ ways of creating a :class:`Matrix_gfpn_dense` instance.
343
+ However, these should only be of internal use.
344
+ """
345
+ ##################
346
+ ## Init, Dealloc, Copy
347
+
348
+ def __dealloc__(self):
349
+ """
350
+ TESTS::
351
+
352
+ sage: MS = MatrixSpace(GF(64), 0)
353
+ sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense
354
+ sage: Matrix_gfpn_dense.__new__(Matrix_gfpn_dense, MS)
355
+ []
356
+ sage: M = None
357
+ sage: M = Matrix_gfpn_dense(MatrixSpace(GF(64,'z'),4), None)
358
+ sage: del M # indirect doctest
359
+ """
360
+ if self.Data != NULL:
361
+ MatFree(self.Data)
362
+
363
+ def __init__(self, parent, entries=None, copy=None, bint coerce=False, *, bint mutable=True):
364
+ r"""
365
+ Matrix extension class using libmeataxe as backend.
366
+
367
+ INPUT:
368
+
369
+ - ``parent`` -- a matrix space over ``GF(q)`` with `q < 255`
370
+
371
+ - ``entries`` -- see :func:`matrix`
372
+
373
+ - ``copy`` -- ignored (for backwards compatibility)
374
+
375
+ - ``coerce`` -- ignored
376
+
377
+ - ``mutable`` -- if ``False``, the resulting matrix cannot be
378
+ changed, and it can be used as key in a Python dictionary
379
+
380
+ EXAMPLES::
381
+
382
+ sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense
383
+
384
+ 1. Creating a zero (3x2)-matrix::
385
+
386
+ sage: Matrix_gfpn_dense(MatrixSpace(GF(4,'z'),3,2))
387
+ [0 0]
388
+ [0 0]
389
+ [0 0]
390
+
391
+ 2. Creating a matrix from a list or list of lists::
392
+
393
+ sage: Matrix_gfpn_dense(MatrixSpace(GF(5),2,3),[1,2,3,4,5,6])
394
+ [1 2 3]
395
+ [4 0 1]
396
+ sage: Matrix_gfpn_dense(MatrixSpace(GF(5),2,3),[[1,2,3],[4,5,6]])
397
+ [1 2 3]
398
+ [4 0 1]
399
+
400
+ 3. Creating a diagonal matrix::
401
+
402
+ sage: M = Matrix_gfpn_dense(MatrixSpace(GF(7),5),2); M
403
+ [2 0 0 0 0]
404
+ [0 2 0 0 0]
405
+ [0 0 2 0 0]
406
+ [0 0 0 2 0]
407
+ [0 0 0 0 2]
408
+
409
+ TESTS::
410
+
411
+ sage: MS = MatrixSpace(GF(125,'y'),2) # indirect doctest
412
+ sage: A = MS(0)
413
+ sage: A.left_kernel()
414
+ Vector space of degree 2 and dimension 2 over Finite Field in y of size 5^3
415
+ Basis matrix:
416
+ [1 0]
417
+ [0 1]
418
+ sage: A.right_kernel()
419
+ Vector space of degree 2 and dimension 2 over Finite Field in y of size 5^3
420
+ Basis matrix:
421
+ [1 0]
422
+ [0 1]
423
+ """
424
+ ma = MatrixArgs_init(parent, entries)
425
+ cdef long fl = ma.base.order()
426
+ cdef long nr = ma.nrows
427
+ cdef long nc = ma.ncols
428
+
429
+ self.Data = MatAlloc(fl, nr, nc)
430
+ self._converter = FieldConverter(ma.base)
431
+
432
+ cdef PTR x = self.Data.Data
433
+ assert self.Data.Noc == nc
434
+ assert self.Data.Nor == nr
435
+ FfSetField(fl)
436
+ FfSetNoc(nc)
437
+ it = ma.iter(False)
438
+ cdef long i,j
439
+ for i in range(nr):
440
+ for j in range(nc):
441
+ c = self._converter.field_to_fel(self._coerce_element(next(it)))
442
+ FfInsert(x, j, c)
443
+ sig_check()
444
+ FfStepPtr(&x)
445
+
446
+ self._is_immutable = not mutable
447
+
448
+ @staticmethod
449
+ def from_filename(filename):
450
+ """
451
+ Create a matrix from a file in MeatAxe format. If the file
452
+ does not exist, an error raised by the MeatAxe library is
453
+ propagated::
454
+
455
+ sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense
456
+ sage: Matrix_gfpn_dense.from_filename('foobarNONEXISTING_FILE')
457
+ Traceback (most recent call last):
458
+ ...
459
+ OSError: foobarNONEXISTING_FILE: No such file or directory in file os.c (line 255)
460
+ sage: Matrix_gfpn_dense.from_filename('')
461
+ Traceback (most recent call last):
462
+ ...
463
+ ValueError: cannot construct meataxe matrix from empty filename
464
+ """
465
+ if not filename:
466
+ raise ValueError("cannot construct meataxe matrix from empty filename")
467
+
468
+ if type(filename) is not bytes:
469
+ filename = filename.encode(FS_ENCODING, 'surrogateescape')
470
+
471
+ sig_on()
472
+ try:
473
+ mat = MatLoad(filename)
474
+ finally:
475
+ sig_off()
476
+ return new_mtx(mat, None)
477
+
478
+ def __copy__(self):
479
+ """
480
+ Return a mutable copy of this matrix.
481
+
482
+ EXAMPLES::
483
+
484
+ sage: M = MatrixSpace(GF(25,'x'), 3, 20)([20*[0],20*[0],[1]+19*[0]])
485
+ sage: N = copy(M) # indirect doctest
486
+ sage: print(N)
487
+ [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
488
+ [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
489
+ [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
490
+ sage: N == M
491
+ True
492
+ sage: N is M
493
+ False
494
+ sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense
495
+ sage: M = Matrix_gfpn_dense.__new__(Matrix_gfpn_dense, parent(M))
496
+ sage: copy(M)
497
+ Traceback (most recent call last):
498
+ ...
499
+ ValueError: cannot copy an uninitialized matrix
500
+ """
501
+ if self.Data is NULL:
502
+ raise ValueError("cannot copy an uninitialized matrix")
503
+ sig_on()
504
+ try:
505
+ mat = MatDup(self.Data)
506
+ finally:
507
+ sig_off()
508
+ retval = new_mtx(mat, self)
509
+ if self._cache:
510
+ retval._cache = dict(self._cache)
511
+ return retval
512
+
513
+ def __reduce__(self):
514
+ """
515
+ TESTS::
516
+
517
+ sage: M = MatrixSpace(GF(9,'x'),10,10).random_element()
518
+ sage: M == loads(dumps(M)) # indirect doctest
519
+ True
520
+ sage: M is loads(dumps(M))
521
+ False
522
+ """
523
+ cdef char* d
524
+ cdef char* x
525
+ cdef size_t i
526
+ cdef PTR p
527
+ cdef size_t pickle_size
528
+ cdef bytes pickle_str
529
+ if self.Data:
530
+ FfSetField(self.Data.Field)
531
+ FfSetNoc(self.Data.Noc)
532
+ pickle_size = FfCurrentRowSizeIo*self.Data.Nor
533
+ d = <char*>check_malloc(pickle_size)
534
+ p = self.Data.Data
535
+ x = d
536
+ for i in range(self.Data.Nor):
537
+ memcpy(x, p, FfCurrentRowSizeIo)
538
+ sig_check()
539
+ x += FfCurrentRowSizeIo
540
+ FfStepPtr(&p)
541
+ pickle_str = PyBytes_FromStringAndSize(d, pickle_size)
542
+ sig_free(d)
543
+ return mtx_unpickle, (self._parent, self.Data.Nor, self.Data.Noc,
544
+ pickle_str,
545
+ not self._is_immutable) # for backward compatibility with the group cohomology package
546
+ else:
547
+ return mtx_unpickle, (0, 0, 0, '', not self._is_immutable)
548
+
549
+ cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j) noexcept:
550
+ """
551
+ Get an element without checking.
552
+
553
+ TESTS::
554
+
555
+ sage: F.<z> = GF(9)
556
+ sage: M = MatrixSpace(F,3)(sorted(list(F)))
557
+ sage: type(M)
558
+ <class 'sage.matrix.matrix_gfpn_dense.Matrix_gfpn_dense'>
559
+ sage: M # indirect doctest
560
+ [ 0 1 2]
561
+ [ z z + 1 z + 2]
562
+ [ 2*z 2*z + 1 2*z + 2]
563
+ """
564
+ if self.Data == NULL:
565
+ raise IndexError("Matrix is empty")
566
+ FfSetField(self.Data.Field)
567
+ return self._converter.fel_to_field(FfExtract(MatGetPtr(self.Data,i), j))
568
+
569
+ cdef inline int get_unsafe_int(self, Py_ssize_t i, Py_ssize_t j) noexcept:
570
+ # NOTE:
571
+ # It is essential that you call FfSetField and FfSetNoc YOURSELF
572
+ # and that you assert that the matrix is not empty!
573
+ # This method is here for speed!
574
+ return FfToInt(FfExtract(MatGetPtr(self.Data,i), j))
575
+
576
+ cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j) except -1:
577
+ r"""
578
+ Return 1 if the entry ``(i, j)`` is zero, otherwise 0.
579
+
580
+ EXAMPLES::
581
+
582
+ sage: F.<z> = GF(9)
583
+ sage: M = MatrixSpace(F,2,5)(sorted(list(F))+[0])
584
+ sage: M.zero_pattern_matrix() # indirect doctest
585
+ [1 0 0 0 0]
586
+ [0 0 0 0 1]
587
+ """
588
+ FfSetField(self.Data.Field)
589
+ return FfExtract(MatGetPtr(self.Data,i), j) == self._converter.zero_FEL
590
+
591
+ cpdef Matrix_gfpn_dense get_slice(self, Py_ssize_t i, Py_ssize_t j) noexcept:
592
+ """
593
+ Return a horizontal slice of this matrix.
594
+
595
+ NOTE:
596
+
597
+ ``M[i:j]`` may return a matrix that uses a different backend than
598
+ MeatAxe. This method is useful when the slice has to be of type
599
+ :class:`Matrix_gfpn_dense`.
600
+
601
+ EXAMPLES::
602
+
603
+ sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
604
+ sage: M = MTX(MatrixSpace(GF(7), 5, 3), [[0,1,2], [1,2,3], [2,3,4], [3,4,5], [4,5,6]])
605
+ sage: M
606
+ [0 1 2]
607
+ [1 2 3]
608
+ [2 3 4]
609
+ [3 4 5]
610
+ [4 5 6]
611
+ sage: M.get_slice(1,3)
612
+ [1 2 3]
613
+ [2 3 4]
614
+ sage: type(_) is MTX
615
+ True
616
+ """
617
+ if not 0 <= i < j <= self.Data.Nor:
618
+ raise IndexError("Indices i={}, j={} violate the condition 0 < i < j < {}".format(i,j,self.Data.Nor))
619
+ sig_on()
620
+ try:
621
+ mat = MatAlloc(self.Data.Field, j-i, self.Data.Noc)
622
+ memcpy(mat.Data, FfGetPtr(self.Data.Data, i), FfCurrentRowSize*(j-i))
623
+ finally:
624
+ sig_off()
625
+ return new_mtx(mat, self)
626
+
627
+ cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value) noexcept:
628
+ """
629
+ Set values without bound checking.
630
+
631
+ TESTS:
632
+
633
+ The following test would have failed in a preliminary version
634
+ of this MeatAxe wrapper::
635
+
636
+ sage: K.<x> = GF(125)
637
+ sage: M = MatrixSpace(K,9,9)()
638
+ sage: N = MatrixSpace(GF(9,'x'),20).random_element()
639
+ sage: M[2,2] = x
640
+ sage: M
641
+ [0 0 0 0 0 0 0 0 0]
642
+ [0 0 0 0 0 0 0 0 0]
643
+ [0 0 x 0 0 0 0 0 0]
644
+ [0 0 0 0 0 0 0 0 0]
645
+ [0 0 0 0 0 0 0 0 0]
646
+ [0 0 0 0 0 0 0 0 0]
647
+ [0 0 0 0 0 0 0 0 0]
648
+ [0 0 0 0 0 0 0 0 0]
649
+ [0 0 0 0 0 0 0 0 0]
650
+ """
651
+ # ASSUMPTION: value's parent is the base ring
652
+ if self.Data == NULL:
653
+ raise IndexError("Matrix is empty")
654
+ FfSetField(self.Data.Field)
655
+ FfInsert(MatGetPtr(self.Data,i), j, self._converter.field_to_fel(value))
656
+
657
+ cdef void set_unsafe_int(self, Py_ssize_t i, Py_ssize_t j, int value) noexcept:
658
+ # NOTE:
659
+ # It is essential that you call FfSetField and FfSetNoc YOURSELF
660
+ # and that you assert that the matrix is not empty!
661
+ # This method is here for speed!
662
+ FfInsert(FfGetPtr(self.Data.Data,i), j, FfFromInt(value))
663
+
664
+ cdef set_slice_unsafe(self, Py_ssize_t i, Matrix_gfpn_dense S) noexcept:
665
+ # Overwrite the self[i:i+S.nrows()] by the contents of S.
666
+ #
667
+ # NOTE:
668
+ # It is essential that you call FfSetField and FfSetNoc YOURSELF
669
+ # and that the dimensions of self and S match!
670
+ sig_on()
671
+ try:
672
+ memcpy(FfGetPtr(self.Data.Data, i), S.Data.Data, FfCurrentRowSize*S.Data.Nor)
673
+ finally:
674
+ sig_off()
675
+
676
+ def randomize(self, density=None, nonzero=False, *args, **kwds):
677
+ """
678
+ Fill the matrix with random values.
679
+
680
+ INPUT:
681
+
682
+ - ``density`` -- (optional) real number between zero and one the
683
+ expected density of the resulting matrix
684
+ - ``nonzero`` -- boolean (default: ``False``); if ``True``, all
685
+ inserted marks are nonzero
686
+
687
+ EXAMPLES::
688
+
689
+ sage: MS = MatrixSpace(GF(27,'z'),6,6)
690
+ sage: M = MS.random_element() # indirect doctest
691
+ sage: type(M)
692
+ <class 'sage.matrix.matrix_gfpn_dense.Matrix_gfpn_dense'>
693
+ sage: M = MS.random_element(nonzero=True)
694
+ sage: all(M[i,j] for i in range(6) for j in range(6))
695
+ True
696
+ sage: avg_density = sum(MS.random_element(density=0.5).density()
697
+ ....: for _ in range(100))
698
+ sage: avg_density /= 100
699
+ sage: RR(avg_density) # random
700
+ 0.5
701
+
702
+ TESTS:
703
+
704
+ The following tests against a bug that was fixed in :issue:`23352`.
705
+ This test could fail for some seed, but it would be highly unlikely::
706
+
707
+ sage: MS = MatrixSpace(GF(9,'x'),1,5)
708
+ sage: any(MS.random_element()[0,4] for _ in range(50))
709
+ True
710
+ """
711
+ self.check_mutability()
712
+ cdef int fl = self.Data.Field
713
+ density = float(density)
714
+ if density <= 0:
715
+ return
716
+ if density > 1:
717
+ density = float(1)
718
+
719
+ self.clear_cache()
720
+
721
+ cdef PTR x
722
+ cdef unsigned char *y
723
+ x = self.Data.Data
724
+ cdef int nr = self.Data.Nor
725
+ cdef int nc = self.Data.Noc
726
+ cdef Py_ssize_t i, j
727
+
728
+ FfSetField(fl)
729
+ FfSetNoc(nc)
730
+ cdef int MPB, tmp
731
+ cdef unsigned char O
732
+ cdef randstate RandState = current_randstate()
733
+
734
+ if not nonzero:
735
+ if density == 1:
736
+ MPB = 0
737
+ tmp = fl
738
+ while tmp <= 256:
739
+ MPB += 1
740
+ tmp *= fl
741
+ O = (fl**MPB)
742
+ if nc % MPB:
743
+ for i in range(nr):
744
+ y = <unsigned char*>x
745
+ for j in range(FfCurrentRowSizeIo-1):
746
+ y[j] = RandState.c_random() % O
747
+ sig_check()
748
+ for j in range(nc-(nc % MPB), nc):
749
+ FfInsert(x, j, FfFromInt( (RandState.c_random() % fl) ))
750
+ sig_check()
751
+ FfStepPtr(&(x))
752
+ else:
753
+ for i in range(nr):
754
+ y = <unsigned char*>x
755
+ for j in range(FfCurrentRowSizeIo):
756
+ y[j] = RandState.c_random() % O
757
+ sig_check()
758
+ FfStepPtr(&(x))
759
+ else:
760
+ for i in range(nr):
761
+ for j in range(nc):
762
+ if RandState.c_rand_double() < density:
763
+ FfInsert(x, j, FfFromInt( (RandState.c_random() % fl) ))
764
+ sig_check()
765
+ FfStepPtr(&(x))
766
+ else:
767
+ if density == 1:
768
+ fl -= 1
769
+ for i in range(nr):
770
+ for j in range(nc):
771
+ FfInsert(x, j, FfFromInt( (RandState.c_random() % fl)+1 ))
772
+ sig_check()
773
+ FfStepPtr(&(x))
774
+ else:
775
+ fl -= 1
776
+ for i in range(nr):
777
+ for j in range(nc):
778
+ if RandState.c_rand_double() < density:
779
+ FfInsert(x, j, FfFromInt( (RandState.c_random() % fl)+1 ))
780
+ sig_check()
781
+ FfStepPtr(&(x))
782
+
783
+ ## Debugging
784
+ # def show_contents(self, r=None):
785
+ # FfSetField(self.Data.Field)
786
+ # FfSetNoc(self.Data.Noc)
787
+ # cdef PTR p
788
+ # cdef size_t i, j
789
+ # if r is not None:
790
+ # r_min = r
791
+ # r_max = r+1
792
+ # else:
793
+ # r_min = 0
794
+ # r_max = self.Data.Nor
795
+ # for i in range(r_min, r_max):
796
+ # p = FfGetPtr(self.Data.Data, i)
797
+ # for j from 0<=j<self.Data.RowSize:
798
+ # print("%3.3d" % p[j])
799
+ # print
800
+
801
+ ##################
802
+ ## comparison
803
+ cpdef _richcmp_(left, right, int op) noexcept:
804
+ """
805
+ Compare two :class:`Matrix_gfpn_dense` matrices.
806
+
807
+ Of course, '<' and '>' do not make much sense for matrices.
808
+
809
+ EXAMPLES::
810
+
811
+ sage: M = MatrixSpace(GF(125,'x'),3,20)([20*[0],20*[0],[1]+19*[0]])
812
+ sage: N = copy(M)
813
+ sage: M == N
814
+ True
815
+ sage: M != N
816
+ False
817
+ sage: M < N
818
+ False
819
+ sage: N[2,19] = 1
820
+ sage: M == N
821
+ False
822
+ sage: M != N
823
+ True
824
+ """
825
+ cdef Matrix_gfpn_dense self = left
826
+ cdef Matrix_gfpn_dense N = right
827
+ if self is None or N is None:
828
+ return rich_to_bool(op, -1)
829
+ if self.Data == NULL:
830
+ if N.Data == NULL:
831
+ return rich_to_bool(op, 0)
832
+ else:
833
+ return rich_to_bool(op, 1)
834
+ elif N.Data == NULL:
835
+ return rich_to_bool(op, -1)
836
+ if self.Data.Field != N.Data.Field:
837
+ if self.Data.Field > N.Data.Field:
838
+ return rich_to_bool(op, 1)
839
+ return rich_to_bool(op, -1)
840
+ if self.Data.Noc != N.Data.Noc:
841
+ if self.Data.Noc > N.Data.Noc:
842
+ return rich_to_bool(op, 1)
843
+ return rich_to_bool(op, -1)
844
+ if self.Data.Nor != N.Data.Nor:
845
+ if self.Data.Nor > N.Data.Nor:
846
+ return rich_to_bool(op, 1)
847
+ return rich_to_bool(op, -1)
848
+
849
+ cdef char* d1 = <char*>self.Data.Data
850
+ cdef char* d2 = <char*>N.Data.Data
851
+ cdef Py_ssize_t total_size = self.Data.RowSize
852
+ total_size *= self.Data.Nor
853
+ cdef bytes s1, s2
854
+ s1 = PyBytes_FromStringAndSize(d1, total_size)
855
+ s2 = PyBytes_FromStringAndSize(d2, total_size)
856
+ if s1 != s2:
857
+ if s1 > s2:
858
+ return rich_to_bool(op, 1)
859
+ return rich_to_bool(op, -1)
860
+ return rich_to_bool(op, 0)
861
+
862
+ cpdef list _rowlist_(self, i, j=-1) noexcept:
863
+ """
864
+ Return rows as a flat list of python ints.
865
+
866
+ INPUT:
867
+
868
+ - ``i`` -- index of the first row to be extracted
869
+ - ``j`` -- (default: -1) -1, or index of the last
870
+ row to be extracted
871
+
872
+ OUTPUT:
873
+
874
+ If `j=-1` then only the `i`-th row is returned as a list.
875
+ Otherwises, rows `i` to `j` (both included) are returned
876
+ as a list of integers.
877
+
878
+ EXAMPLES::
879
+
880
+ sage: k.<x> = GF(25,'x')
881
+ sage: M = random_matrix(GF(25,'x'), 5,5)
882
+ sage: M = matrix(k,[[ 4, 4*x, x + 3, 4*x + 2, 3*x + 4],
883
+ ....: [ x + 2, 3*x + 1, 3, 0, 3],
884
+ ....: [ 3*x, 2*x + 4, 1, 0, 2*x],
885
+ ....: [4*x + 4, 2*x + 3, 4*x, 1, 3*x + 1],
886
+ ....: [3*x + 3, x + 3, x + 2, x + 1, 3*x + 2]])
887
+ sage: M._rowlist_(1)
888
+ [7, 16, 3, 0, 3]
889
+ sage: [M[1,i]._int_repr() for i in range(5)]
890
+ ['7', '16', '3', '0', '3']
891
+ sage: M._rowlist_(2,4)
892
+ [15, 14, 1, 0, 10, 24, 13, 20, 1, 16, 18, 8, 7, 6, 17]
893
+ sage: [[M[i,j]._int_repr() for j in range(5)] for i in range(2,5)]
894
+ [['15', '14', '1', '0', '10'],
895
+ ['24', '13', '20', '1', '16'],
896
+ ['18', '8', '7', '6', '17']]
897
+ """
898
+ cdef int k
899
+ if self.Data:
900
+ FfSetField(self.Data.Field)
901
+ FfSetNoc(self.Data.Noc)
902
+ else:
903
+ raise ValueError("Matrix is empty")
904
+ if (i < 0) or (i >= self.Data.Nor):
905
+ raise IndexError("Index {} out of range 0..{}",format(i, self.Data.Nor-1))
906
+ cdef PTR p
907
+ p = MatGetPtr(self.Data, i)
908
+ L = [FfToInt(FfExtract(p, k)) for k in range(self.Data.Noc)]
909
+ if j != -1:
910
+ if not isinstance(j, (int, Integer)):
911
+ raise TypeError("Second index must be an integer")
912
+ if j >= self.Data.Nor:
913
+ raise IndexError("Index out of range")
914
+ for k in range(i, j):
915
+ FfStepPtr(&(p)) # This is only called after MatGetPtr, hence, after FfSetNoc.
916
+ L.extend([FfToInt(FfExtract(p,l)) for l in range(self.Data.Noc)])
917
+ return L
918
+
919
+ def _list(self):
920
+ """
921
+ Return a flat list of all entries of this matrix.
922
+
923
+ The result is cached.
924
+
925
+ EXAMPLES::
926
+
927
+ sage: MatrixSpace(GF(9,'x'),3)(sorted(list(GF(9,'x')))).list() # indirect doctest
928
+ [0, 1, 2, x, x + 1, x + 2, 2*x, 2*x + 1, 2*x + 2]
929
+
930
+ TESTS:
931
+
932
+ This works correctly on matrices with zero rows or columns::
933
+
934
+ sage: M = Matrix(GF(9), 0, 3); M._list()
935
+ []
936
+ sage: M = Matrix(GF(9), 3, 0); M._list()
937
+ []
938
+ sage: M = Matrix(GF(9), 0, 0); M._list()
939
+ []
940
+ """
941
+ L = self.fetch('list')
942
+ if L is not None:
943
+ return L
944
+ if self.Data is NULL:
945
+ raise IndexError("matrix is empty")
946
+ FfSetField(self.Data.Field)
947
+ FfSetNoc(self.Data.Noc)
948
+ cdef list x = []
949
+ cdef int i, j
950
+ p = self.Data.Data
951
+ for i in range(self.Data.Nor):
952
+ sig_check()
953
+ for j in range(self.Data.Noc):
954
+ x.append(self._converter.fel_to_field(FfExtract(p,j)))
955
+ FfStepPtr(&p)
956
+ self.cache('list', x)
957
+ return x
958
+
959
+ #########################
960
+ ## Arithmetics
961
+ cdef rescale_row_c(self, Py_ssize_t i, s, Py_ssize_t start_col) noexcept:
962
+ """
963
+ Rescale row number `i` in-place by multiplication with the scalar `s`.
964
+
965
+ The scalar `s` is converted into the base ring.
966
+
967
+ EXAMPLES::
968
+
969
+ sage: K.<x> = GF(25)
970
+ sage: M = MatrixSpace(K,5,5)(sorted(list(K)))
971
+ sage: M
972
+ [ 0 1 2 3 4]
973
+ [ x x + 1 x + 2 x + 3 x + 4]
974
+ [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4]
975
+ [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4]
976
+ [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4]
977
+ sage: M.rescale_row(1, 3) # indirect doctest
978
+ sage: M
979
+ [ 0 1 2 3 4]
980
+ [ 3*x 3*x + 3 3*x + 1 3*x + 4 3*x + 2]
981
+ [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4]
982
+ [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4]
983
+ [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4]
984
+ sage: M.rescale_row(4, x)
985
+ sage: M
986
+ [ 0 1 2 3 4]
987
+ [ 3*x 3*x + 3 3*x + 1 3*x + 4 3*x + 2]
988
+ [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4]
989
+ [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4]
990
+ [4*x + 2 2 x + 2 2*x + 2 3*x + 2]
991
+
992
+ TESTS:
993
+
994
+ The following tests against bugs fixed in :issue:`25490`. It shows
995
+ that the optional argument ``start_col`` is correctly dealt with,
996
+ in an example where several marks are packed into one byte, and
997
+ so that temporarily the current field and row size are changed.
998
+ ::
999
+
1000
+ sage: L.<y> = GF(9)
1001
+ sage: N = MatrixSpace(L,5,5)((3*sorted(list(L)))[:25])
1002
+ sage: N
1003
+ [ 0 1 2 y y + 1]
1004
+ [ y + 2 2*y 2*y + 1 2*y + 2 0]
1005
+ [ 1 2 y y + 1 y + 2]
1006
+ [ 2*y 2*y + 1 2*y + 2 0 1]
1007
+ [ 2 y y + 1 y + 2 2*y]
1008
+ sage: M = MatrixSpace(K,1,25)(sorted(list(K)))
1009
+ sage: N.rescale_row(1,1/y,1)
1010
+ sage: N
1011
+ [ 0 1 2 y y + 1]
1012
+ [ y + 2 2 y + 1 2*y 0]
1013
+ [ 1 2 y y + 1 y + 2]
1014
+ [ 2*y 2*y + 1 2*y + 2 0 1]
1015
+ [ 2 y y + 1 y + 2 2*y]
1016
+ """
1017
+ if self.Data == NULL or start_col >= self.Data.Noc:
1018
+ return
1019
+ FfSetField(self.Data.Field)
1020
+ cdef FEL c = self._converter.field_to_fel(self._base_ring(s))
1021
+ cdef ssize_t byte_offset = start_col//MPB # how many full bytes will remain unchanged?
1022
+ cdef ssize_t remains = start_col % MPB # how many cols have to be treated separately?
1023
+ cdef ssize_t noc # what bunch of cols will be treated together?
1024
+ cdef int j
1025
+ cdef PTR row_head = MatGetPtr(self.Data, i) + byte_offset
1026
+ if remains:
1027
+ for j in range(remains,MPB):
1028
+ FfInsert(row_head, j, FfMul(FfExtract(row_head,j), c))
1029
+ byte_offset += 1
1030
+ row_head += 1
1031
+ noc = self.Data.Noc - byte_offset*MPB
1032
+ if noc:
1033
+ FfSetNoc(noc)
1034
+ FfMulRow(row_head, c)
1035
+
1036
+ cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple, Py_ssize_t start_col) noexcept:
1037
+ """
1038
+ Add the ``multiple``-fold of row ``row_from`` in-place to row ``row_to``, beginning with ``start_col``
1039
+
1040
+ EXAMPLES::
1041
+
1042
+ sage: K.<x> = GF(25)
1043
+ sage: M = MatrixSpace(K,5,5)(sorted(list(K)))
1044
+ sage: M
1045
+ [ 0 1 2 3 4]
1046
+ [ x x + 1 x + 2 x + 3 x + 4]
1047
+ [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4]
1048
+ [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4]
1049
+ [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4]
1050
+ sage: M.add_multiple_of_row(2, 4, x) # indirect doctest
1051
+ sage: M
1052
+ [ 0 1 2 3 4]
1053
+ [ x x + 1 x + 2 x + 3 x + 4]
1054
+ [ x + 2 2*x + 3 3*x + 4 4*x 1]
1055
+ [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4]
1056
+ [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4]
1057
+
1058
+ TESTS:
1059
+
1060
+ The following tests against bugs fixed at :issue:`25490`. It demonstrates
1061
+ that the optional argument ``start_col`` is correctly dealt with,
1062
+ in a situation where several marks are packed into one byte
1063
+ and the current field and row size are temporarily changed.
1064
+ ::
1065
+
1066
+ sage: L.<y> = GF(9)
1067
+ sage: N = MatrixSpace(L,5,5)((3*sorted(list(L)))[:25])
1068
+ sage: N
1069
+ [ 0 1 2 y y + 1]
1070
+ [ y + 2 2*y 2*y + 1 2*y + 2 0]
1071
+ [ 1 2 y y + 1 y + 2]
1072
+ [ 2*y 2*y + 1 2*y + 2 0 1]
1073
+ [ 2 y y + 1 y + 2 2*y]
1074
+ sage: M = MatrixSpace(K,1,25)(sorted(list(K)))
1075
+ sage: N.add_multiple_of_row(2,1,-N[2,1]/N[1,1],1)
1076
+ sage: N
1077
+ [ 0 1 2 y y + 1]
1078
+ [ y + 2 2*y 2*y + 1 2*y + 2 0]
1079
+ [ 1 0 2 2*y + 1 y + 2]
1080
+ [ 2*y 2*y + 1 2*y + 2 0 1]
1081
+ [ 2 y y + 1 y + 2 2*y]
1082
+ sage: y-(2*y+1)/y
1083
+ 2
1084
+ """
1085
+ if self.Data == NULL or start_col >= self.Data.Noc:
1086
+ return
1087
+ FfSetField(self.Data.Field)
1088
+ cdef FEL c = self._converter.field_to_fel(self._base_ring(multiple))
1089
+ cdef ssize_t byte_offset = start_col//MPB # how many full bytes will remain unchanged?
1090
+ cdef ssize_t remains = start_col % MPB # how many cols have to be treated separately?
1091
+ cdef ssize_t noc # what bunch of cols will be treated together?
1092
+ cdef PTR row_to_head = MatGetPtr(self.Data, row_to)+byte_offset
1093
+ cdef PTR row_from_head = MatGetPtr(self.Data, row_from)+byte_offset
1094
+ if remains:
1095
+ for j in range(remains,MPB):
1096
+ FfInsert(row_to_head, j, FfAdd(FfExtract(row_to_head,j), FfMul(FfExtract(row_from_head,j), c)))
1097
+ byte_offset += 1
1098
+ row_to_head += 1
1099
+ row_from_head += 1
1100
+ noc = self.Data.Noc - byte_offset*MPB
1101
+ if noc:
1102
+ FfSetNoc(noc)
1103
+ FfAddMulRow(row_to_head, row_from_head, c)
1104
+
1105
+ cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2) noexcept:
1106
+ """
1107
+ Swap the rows ``row1`` and ``row2`` in-place.
1108
+
1109
+ EXAMPLES::
1110
+
1111
+ sage: K.<x> = GF(25)
1112
+ sage: M = MatrixSpace(K,5,5)(sorted(list(K)))
1113
+ sage: M
1114
+ [ 0 1 2 3 4]
1115
+ [ x x + 1 x + 2 x + 3 x + 4]
1116
+ [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4]
1117
+ [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4]
1118
+ [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4]
1119
+ sage: M.swap_rows(1, 3) # indirect doctest
1120
+ sage: M
1121
+ [ 0 1 2 3 4]
1122
+ [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4]
1123
+ [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4]
1124
+ [ x x + 1 x + 2 x + 3 x + 4]
1125
+ [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4]
1126
+ """
1127
+ if not self.Data:
1128
+ raise ValueError("This matrix is empty")
1129
+ FfSetField(self.Data.Field)
1130
+ FfSetNoc(self.Data.Noc)
1131
+ FfSwapRows(MatGetPtr(self.Data, row1), MatGetPtr(self.Data, row2))
1132
+
1133
+ def trace(self):
1134
+ """
1135
+ Trace of this matrix, i.e., the sum of diagonal elements.
1136
+
1137
+ EXAMPLES::
1138
+
1139
+ sage: K.<x> = GF(125)
1140
+ sage: MatrixSpace(K,7,7)(x).trace()
1141
+ 2*x
1142
+ """
1143
+ if self._nrows != self._ncols:
1144
+ raise ValueError("self must be a square matrix")
1145
+ return self._converter.fel_to_field(MatTrace(self.Data))
1146
+
1147
+ cdef _stack_impl(self, bottom) noexcept:
1148
+ r"""
1149
+ Stack ``self`` on top of ``bottom``.
1150
+
1151
+ INPUT:
1152
+
1153
+ - ``bottom`` -- a matrix with the same number of columns as ``self``
1154
+
1155
+ EXAMPLES::
1156
+
1157
+ sage: M = MatrixSpace(GF(9,'x'),1,9)(sorted(list(GF(9,'x'))))
1158
+ sage: M
1159
+ [ 0 1 2 x x + 1 x + 2 2*x 2*x + 1 2*x + 2]
1160
+ sage: M.stack(M)
1161
+ [ 0 1 2 x x + 1 x + 2 2*x 2*x + 1 2*x + 2]
1162
+ [ 0 1 2 x x + 1 x + 2 2*x 2*x + 1 2*x + 2]
1163
+
1164
+ Check that we can stack a vector (:issue:`31708`)::
1165
+
1166
+ sage: R.<a> = GF(3^3)
1167
+ sage: M = matrix(R, [[1,1],[0,a+1]])
1168
+ sage: M.stack(vector(R, [a,0]))
1169
+ [ 1 1]
1170
+ [ 0 a + 1]
1171
+ [ a 0]
1172
+ """
1173
+ cdef Matrix_gfpn_dense other = <Matrix_gfpn_dense> bottom
1174
+
1175
+ if self._nrows == 0 or self.Data == NULL:
1176
+ return other.__copy__()
1177
+ if other._nrows == 0 or other.Data == NULL:
1178
+ return self.__copy__()
1179
+ sig_on()
1180
+ try:
1181
+ mat = MatAlloc(self.Data.Field, self.Data.Nor+other.Data.Nor, self.Data.Noc)
1182
+ memcpy(mat.Data, self.Data.Data, FfCurrentRowSize*self.Data.Nor)
1183
+ memcpy(MatGetPtr(mat, self.Data.Nor), other.Data.Data, FfCurrentRowSize*other.Data.Nor)
1184
+ finally:
1185
+ sig_off()
1186
+ return new_mtx(mat, self)
1187
+
1188
+ cpdef _add_(self, right) noexcept:
1189
+ """
1190
+ TESTS::
1191
+
1192
+ sage: K.<x> = GF(9)
1193
+ sage: M = MatrixSpace(K,3,3)(sorted(list(K)))
1194
+ sage: N = MatrixSpace(K,3,3)(2*x)
1195
+ sage: M+N # indirect doctest
1196
+ [ 2*x 1 2]
1197
+ [ x 1 x + 2]
1198
+ [ 2*x 2*x + 1 x + 2]
1199
+ """
1200
+ cdef Matrix_gfpn_dense Self = self
1201
+ cdef Matrix_gfpn_dense Right = right
1202
+ assert Self is not None
1203
+ assert Right is not None
1204
+ if Self.Data == NULL or Right.Data == NULL:
1205
+ raise NotImplementedError("The matrices must not be empty")
1206
+ sig_on()
1207
+ try:
1208
+ mat = MatDup(Self.Data)
1209
+ MatAdd(mat, Right.Data)
1210
+ finally:
1211
+ sig_off()
1212
+ return new_mtx(mat, self)
1213
+
1214
+ cpdef _sub_(self, right) noexcept:
1215
+ """
1216
+ TESTS::
1217
+
1218
+ sage: K.<x> = GF(9)
1219
+ sage: M = MatrixSpace(K,3,3)(sorted(list(K)))
1220
+ sage: N = MatrixSpace(K,3,3)(2*x)
1221
+ sage: M-N # indirect doctest
1222
+ [ x 1 2]
1223
+ [ x 2*x + 1 x + 2]
1224
+ [ 2*x 2*x + 1 2]
1225
+ """
1226
+ cdef Matrix_gfpn_dense Self = self
1227
+ cdef Matrix_gfpn_dense Right = right
1228
+ assert Self is not None
1229
+ assert Right is not None
1230
+ if Self.Data == NULL or Right.Data == NULL:
1231
+ raise NotImplementedError("The matrices must not be empty")
1232
+ sig_on()
1233
+ try:
1234
+ mat = MatDup(Self.Data)
1235
+ MatAddMul(mat, Right.Data, mtx_taddinv[1])
1236
+ finally:
1237
+ sig_off()
1238
+ return new_mtx(mat, self)
1239
+
1240
+ def __neg__(self):
1241
+ """
1242
+ TESTS::
1243
+
1244
+ sage: M = MatrixSpace(GF(9,'x'),3,3)(sorted(list(GF(9,'x'))))
1245
+ sage: -M
1246
+ [ 0 2 1]
1247
+ [ 2*x 2*x + 2 2*x + 1]
1248
+ [ x x + 2 x + 1]
1249
+
1250
+ ::
1251
+
1252
+ sage: M = MatrixSpace(GF(125,'x'),10,30).random_element()
1253
+ sage: N = MatrixSpace(GF(125,'x'),10,30).random_element()
1254
+ sage: M + (-N) == M - N == -(N - M)
1255
+ True
1256
+ """
1257
+ if self.Data == NULL:
1258
+ raise ValueError("The matrix must not be empty")
1259
+ return self._lmul_(self._base_ring(-1))
1260
+
1261
+ cpdef _lmul_(self, Element right) noexcept:
1262
+ """
1263
+ EXAMPLES::
1264
+
1265
+ sage: M = MatrixSpace(GF(9,'x'),3,3)(sorted(list(GF(9,'x'))))
1266
+ sage: K.<x> = GF(9)
1267
+ sage: M = MatrixSpace(K,3,3)(sorted(list(K)))
1268
+ sage: x*M # indirect doctest
1269
+ [ 0 x 2*x]
1270
+ [ x + 1 2*x + 1 1]
1271
+ [2*x + 2 2 x + 2]
1272
+ sage: M*x # indirect doctest
1273
+ [ 0 x 2*x]
1274
+ [ x + 1 2*x + 1 1]
1275
+ [2*x + 2 2 x + 2]
1276
+ sage: -M == (-1)*M
1277
+ True
1278
+
1279
+ TESTS:
1280
+
1281
+ Make sure that :issue:`25076` remains fixed::
1282
+
1283
+ sage: M == M*int(4) == int(4)*M
1284
+ True
1285
+ """
1286
+ if self.Data == NULL:
1287
+ return self.__copy__()
1288
+ FfSetField(self.Data.Field)
1289
+ sig_on()
1290
+ try:
1291
+ mat = MatDup(self.Data)
1292
+ MatMulScalar(mat, self._converter.field_to_fel(right))
1293
+ finally:
1294
+ sig_off()
1295
+ return new_mtx(mat, self)
1296
+
1297
+ cdef int _strassen_default_cutoff(self, sage.matrix.matrix0.Matrix right) except -2:
1298
+ # Surprisingly, Winograd-Strassen can compete with school book
1299
+ # multiplication for smallish matrices, and of course it is
1300
+ # asymptotically faster. So, we used it by default.
1301
+ return 0
1302
+
1303
+ cpdef Matrix_gfpn_dense _multiply_classical(Matrix_gfpn_dense self, Matrix_gfpn_dense right) noexcept:
1304
+ """
1305
+ Multiplication using the cubic school book multiplication algorithm.
1306
+
1307
+ EXAMPLES:
1308
+
1309
+ Since by default the asymptotically faster Strassen-Winograd
1310
+ multiplication algorithm is used, the following is a valid
1311
+ consistency check::
1312
+
1313
+ sage: M = MatrixSpace(GF(9,'x'),1000,500).random_element()
1314
+ sage: N = MatrixSpace(GF(9,'x'),500,2000).random_element()
1315
+ sage: M*N == M._multiply_classical(N)
1316
+ True
1317
+ """
1318
+ "multiply two meataxe matrices by the school book algorithm"
1319
+ if self.Data == NULL or right.Data == NULL:
1320
+ raise ValueError("The matrices must not be empty")
1321
+ if self._ncols != right._nrows:
1322
+ raise ArithmeticError("left ncols must match right nrows")
1323
+ sig_on()
1324
+ try:
1325
+ mat = MatDup(self.Data)
1326
+ MatMul(mat, right.Data)
1327
+ finally:
1328
+ sig_off()
1329
+ return new_mtx(mat, self)
1330
+
1331
+ cpdef Matrix_gfpn_dense _multiply_strassen(Matrix_gfpn_dense self, Matrix_gfpn_dense right, cutoff=0) noexcept:
1332
+ """
1333
+ Matrix multiplication using the asymptotically fast Strassen-Winograd algorithm.
1334
+
1335
+ INPUT:
1336
+
1337
+ - ``right`` -- a matrix of dimensions suitable to do multiplication
1338
+ - ``cutoff`` -- (optional integer) indicates the minimal size of submatrices
1339
+ that will be considered in the divide-and-conquer algorithm. The size is
1340
+ *not* expressed by the number of rows/columns, but the rowsize expressed
1341
+ in bytes. Depending on the base field, one byte may represent up to eight
1342
+ entries in a matrix row. The default is ``sizeof(long)^2/2`` byte.
1343
+
1344
+ EXAMPLES:
1345
+
1346
+ We test that different cutoffs yield the same result::
1347
+
1348
+ sage: M = MatrixSpace(GF(9,'x'),1500,600).random_element()
1349
+ sage: N = MatrixSpace(GF(9,'x'),600,1500).random_element()
1350
+ sage: M._multiply_strassen(N) == M._multiply_strassen(N,80) == M._multiply_strassen(N,2)
1351
+ True
1352
+ """
1353
+ if self.Data == NULL or right.Data == NULL:
1354
+ raise ValueError("The matrices must not be empty")
1355
+ if self._ncols != right._nrows:
1356
+ raise ArithmeticError("left ncols must match right nrows")
1357
+ StrassenSetCutoff(cutoff // sizeof(long))
1358
+ sig_on()
1359
+ try:
1360
+ mat = MatAlloc(self.Data.Field, self._nrows, right._ncols)
1361
+ MatMulStrassen(mat, self.Data, right.Data)
1362
+ finally:
1363
+ sig_off()
1364
+ return new_mtx(mat, self)
1365
+
1366
+ cdef _mul_long(self, long n) noexcept:
1367
+ """
1368
+ Multiply an MTX matrix with a field element represented by an integer.
1369
+
1370
+ TESTS::
1371
+
1372
+ sage: M = random_matrix(GF(9,'x'), 64,51)
1373
+ sage: M == M*int(4) == int(4)*M
1374
+ True
1375
+ sage: M*int(-1)+M == 0
1376
+ True
1377
+ """
1378
+ if self.Data == NULL:
1379
+ raise ValueError("The matrix must not be empty")
1380
+ FfSetField(self.Data.Field)
1381
+ cdef FEL r
1382
+ with cython.cdivision(False):
1383
+ r = FfFromInt(n % FfChar)
1384
+ sig_on()
1385
+ try:
1386
+ mat = MatDup(self.Data)
1387
+ MatMulScalar(mat, r)
1388
+ finally:
1389
+ sig_off()
1390
+ return new_mtx(mat, self)
1391
+
1392
+ def __truediv__(Matrix_gfpn_dense self, p):
1393
+ """
1394
+ Divide a matrix by a scalar.
1395
+
1396
+ EXAMPLES::
1397
+
1398
+ sage: K.<x> = GF(9)
1399
+ sage: M = MatrixSpace(K,3,3)(sorted(list(K)))
1400
+ sage: M
1401
+ [ 0 1 2]
1402
+ [ x x + 1 x + 2]
1403
+ [ 2*x 2*x + 1 2*x + 2]
1404
+ sage: M/2 # indirect doctest
1405
+ [ 0 2 1]
1406
+ [ 2*x 2*x + 2 2*x + 1]
1407
+ [ x x + 2 x + 1]
1408
+ sage: M/x
1409
+ [ 0 x + 2 2*x + 1]
1410
+ [ 1 x 2*x + 2]
1411
+ [ 2 x + 1 2*x]
1412
+ """
1413
+ if self.Data == NULL:
1414
+ return self.__copy__()
1415
+ if not p:
1416
+ raise ZeroDivisionError
1417
+ if p not in self._base_ring:
1418
+ raise ValueError("{} is not a scalar".format(p))
1419
+ p = self._base_ring(p)
1420
+ FfSetField(self.Data.Field)
1421
+ cdef FEL r = mtx_tmultinv[self._converter.field_to_fel(p)]
1422
+ sig_on()
1423
+ try:
1424
+ mat = MatDup(self.Data)
1425
+ MatMulScalar(mat, r)
1426
+ finally:
1427
+ sig_off()
1428
+ return new_mtx(mat, self)
1429
+
1430
+ def __invert__(Matrix_gfpn_dense self):
1431
+ """
1432
+ Multiplicative inverse of this matrix (if available).
1433
+
1434
+ TESTS::
1435
+
1436
+ sage: MS = MatrixSpace(GF(9,'x'),500)
1437
+ sage: while 1:
1438
+ ....: M = MS.random_element()
1439
+ ....: if M.rank() == 500:
1440
+ ....: break
1441
+ sage: Minv = ~M # indirect doctest
1442
+ sage: Minv*M == M*Minv == 1
1443
+ True
1444
+
1445
+ We use the occasion to demonstrate that errors in MeatAxe are
1446
+ correctly handled in Sage::
1447
+
1448
+ sage: MS = MatrixSpace(GF(25,'x'),5)
1449
+ sage: while 1:
1450
+ ....: M = MS.random_element(density=0.4)
1451
+ ....: if M.rank() < 5:
1452
+ ....: break
1453
+ sage: ~M
1454
+ Traceback (most recent call last):
1455
+ ...
1456
+ ZeroDivisionError: Division by zero in file matinv.c (line 50)
1457
+ """
1458
+ if self.Data == NULL:
1459
+ raise ValueError("The matrix must not be empty")
1460
+ if not self.is_square():
1461
+ raise ArithmeticError("self must be a square matrix")
1462
+ sig_on()
1463
+ try:
1464
+ mat = MatInverse(self.Data)
1465
+ finally:
1466
+ # Inverting a singular matrix causes MatInverse to raise a
1467
+ # ZeroDivisionError (see sage_meataxe_error_handler).
1468
+ # So we need to support exceptions here.
1469
+ sig_off()
1470
+ return new_mtx(mat, self)
1471
+
1472
+ def transpose(Matrix_gfpn_dense self):
1473
+ """
1474
+ Return the transposed matrix.
1475
+
1476
+ EXAMPLES::
1477
+
1478
+ sage: K.<x> = GF(9)
1479
+ sage: M = MatrixSpace(K, 2,4)(sorted(list(K)[1:]))
1480
+ sage: M
1481
+ [ 1 2 x x + 1]
1482
+ [ x + 2 2*x 2*x + 1 2*x + 2]
1483
+ sage: M.transpose()
1484
+ [ 1 x + 2]
1485
+ [ 2 2*x]
1486
+ [ x 2*x + 1]
1487
+ [ x + 1 2*x + 2]
1488
+ """
1489
+ if self.Data == NULL:
1490
+ raise ValueError("The matrix must not be empty")
1491
+ sig_on()
1492
+ try:
1493
+ mat = MatTransposed(self.Data)
1494
+ finally:
1495
+ sig_off()
1496
+ return new_mtx(mat, self)
1497
+
1498
+ def order(self):
1499
+ """
1500
+ Return the multiplicative order of this matrix.
1501
+
1502
+ EXAMPLES::
1503
+
1504
+ sage: K.<x> = GF(27)
1505
+ sage: M = MatrixSpace(K, 4)([2*x^2 + 2*x, 2*x^2 + x, 2*x^2 + x + 1,
1506
+ ....: x^2 + x + 2, x + 2, x^2, 2*x + 2, 2*x^2 + 2*x, 2*x^2 + 1,
1507
+ ....: 1, 2, x^2 + 2*x + 1, x^2 + x + 2, x + 1, 2*x^2 + 2*x, x^2 + x])
1508
+ sage: M.order()
1509
+ 104
1510
+ sage: M^104 == 1
1511
+ True
1512
+ sage: M^103 == 1
1513
+ False
1514
+ """
1515
+ if self.Data == NULL:
1516
+ raise ValueError("The matrix must not be empty")
1517
+ if self.Data.Nor != self.Data.Noc:
1518
+ raise ValueError("only defined for square matrices")
1519
+ sig_on()
1520
+ try:
1521
+ o = MatOrder(self.Data)
1522
+ finally:
1523
+ sig_off()
1524
+ if o == -1:
1525
+ raise ArithmeticError("order too large")
1526
+ else:
1527
+ return o
1528
+
1529
+ ###################
1530
+ ## Gauss algorithm
1531
+
1532
+ def left_kernel_matrix(self):
1533
+ """
1534
+ Return the null space of this matrix, represented as a matrix.
1535
+
1536
+ NOTE:
1537
+
1538
+ - For a matrix `M`, ``M.left_kernel_matrix()*M`` is a null matrix.
1539
+ - The command `M.left_kernel()` uses a generic implementation in Sage,
1540
+ that relies on computing the echelon form of the transposed
1541
+ matrix. This method however uses a MeatAxe function to compute
1542
+ the left kernel matrix.
1543
+
1544
+ EXAMPLES::
1545
+
1546
+ sage: K.<x> = GF(25)
1547
+ sage: M = MatrixSpace(K, 10)()
1548
+ sage: entries = [((0, 2), x), ((0, 4), 3*x + 2),
1549
+ ....: ((0, 8), 2*x), ((1, 1), x + 3), ((1, 5), 3*x),
1550
+ ....: ((1, 6), x + 4), ((2, 3), 2*x), ((2, 5), 4*x + 1),
1551
+ ....: ((2, 6), 4), ((3, 4), x + 4), ((3, 5), x + 1),
1552
+ ....: ((5, 5), 3*x), ((5, 7), x + 3), ((6, 1), x),
1553
+ ....: ((6, 2), x + 1), ((6, 5), x + 1), ((8, 2), 4),
1554
+ ....: ((8, 8), 4), ((8, 9), x + 3), ((9, 8), 4*x + 2)]
1555
+ sage: for (i,j),v in entries: M[i,j] = v
1556
+ sage: M.left_kernel()
1557
+ Vector space of degree 10 and dimension 2 over Finite Field in x of size 5^2
1558
+ Basis matrix:
1559
+ [0 0 0 0 1 0 0 0 0 0]
1560
+ [0 0 0 0 0 0 0 1 0 0]
1561
+ sage: M.left_kernel_matrix()
1562
+ [0 0 0 0 1 0 0 0 0 0]
1563
+ [0 0 0 0 0 0 0 1 0 0]
1564
+ """
1565
+ cdef Matrix_gfpn_dense OUT = self.fetch("left_kernel_matrix")
1566
+ if OUT is not None:
1567
+ return OUT
1568
+ if self.Data is NULL:
1569
+ raise ValueError("The matrix must not be empty")
1570
+ sig_on()
1571
+ try:
1572
+ mat = MatNullSpace(self.Data)
1573
+ finally:
1574
+ sig_off()
1575
+ OUT = new_mtx(mat, self)
1576
+ self.cache("left_kernel_matrix", OUT)
1577
+ return OUT
1578
+
1579
+ cpdef _echelon_in_place(self, str algorithm) noexcept:
1580
+ """
1581
+ Change this matrix into echelon form, using classical
1582
+ Gaussian elimination, and return the pivots.
1583
+
1584
+ .. NOTE::
1585
+
1586
+ Use :meth:`_echelon_in_place_classical`, which can take the
1587
+ keyword ``reduced``.
1588
+
1589
+ EXAMPLES::
1590
+
1591
+ sage: K.<x> = GF(25)
1592
+ sage: M = MatrixSpace(K, 9, 3)()
1593
+ sage: entries = [((0, 2), x), ((0, 4), 3*x + 2),
1594
+ ....: ((0, 8), 2*x), ((1, 1), x + 3), ((1, 5), 3*x),
1595
+ ....: ((1, 6), x + 4), ((2, 0), 2*x), ((2, 5), 4*x + 1)]
1596
+ sage: for (i,j),v in entries: M[j,i] = v
1597
+ sage: M
1598
+ [ 0 0 2*x]
1599
+ [ 0 x + 3 0]
1600
+ [ x 0 0]
1601
+ [ 0 0 0]
1602
+ [3*x + 2 0 0]
1603
+ [ 0 3*x 4*x + 1]
1604
+ [ 0 x + 4 0]
1605
+ [ 0 0 0]
1606
+ [ 2*x 0 0]
1607
+ sage: M._echelon_in_place("classical")
1608
+ (0, 1, 2)
1609
+ sage: M
1610
+ [1 0 0]
1611
+ [0 1 0]
1612
+ [0 0 1]
1613
+ [0 0 0]
1614
+ [0 0 0]
1615
+ [0 0 0]
1616
+ [0 0 0]
1617
+ [0 0 0]
1618
+ [0 0 0]
1619
+ """
1620
+ return self._echelon_in_place_classical()
1621
+
1622
+ def _echelon_in_place_classical(self, bint reduced=True, **kwds):
1623
+ """
1624
+ Change this matrix into echelon form, using classical Gaussian elimination
1625
+ and return its pivots.
1626
+
1627
+ INPUT:
1628
+
1629
+ - ``reduced`` -- boolean (default: ``True``); will result
1630
+ in the row-reduced echelon form (otherwise, only a
1631
+ semi-echelon form results)
1632
+
1633
+ EXAMPLES::
1634
+
1635
+ sage: K.<x> = GF(25)
1636
+ sage: M = MatrixSpace(K, 10)()
1637
+ sage: entries = [((0, 2), x), ((0, 4), 3*x + 2),
1638
+ ....: ((0, 8), 2*x), ((1, 1), x + 3), ((1, 5), 3*x),
1639
+ ....: ((1, 6), x + 4), ((2, 3), 2*x), ((2, 5), 4*x + 1),
1640
+ ....: ((2, 6), 4), ((3, 4), x + 4), ((3, 5), x + 1),
1641
+ ....: ((5, 5), 3*x), ((5, 7), x + 3), ((6, 1), x),
1642
+ ....: ((6, 2), x + 1), ((6, 5), x + 1), ((8, 2), 4),
1643
+ ....: ((8, 8), 4), ((8, 9), x + 3), ((9, 8), 4*x + 2)]
1644
+ sage: for (i,j),v in entries: M[i,j] = v
1645
+ sage: M
1646
+ [ 0 0 x 0 3*x + 2 0 0 0 2*x 0]
1647
+ [ 0 x + 3 0 0 0 3*x x + 4 0 0 0]
1648
+ [ 0 0 0 2*x 0 4*x + 1 4 0 0 0]
1649
+ [ 0 0 0 0 x + 4 x + 1 0 0 0 0]
1650
+ [ 0 0 0 0 0 0 0 0 0 0]
1651
+ [ 0 0 0 0 0 3*x 0 x + 3 0 0]
1652
+ [ 0 x x + 1 0 0 x + 1 0 0 0 0]
1653
+ [ 0 0 0 0 0 0 0 0 0 0]
1654
+ [ 0 0 4 0 0 0 0 0 4 x + 3]
1655
+ [ 0 0 0 0 0 0 0 0 4*x + 2 0]
1656
+ sage: M.echelon_form() # indirect doctest
1657
+ [ 0 1 0 0 0 0 0 0 0 4*x + 4]
1658
+ [ 0 0 1 0 0 0 0 0 0 4*x + 2]
1659
+ [ 0 0 0 1 0 0 0 0 0 3*x + 4]
1660
+ [ 0 0 0 0 1 0 0 0 0 3*x + 3]
1661
+ [ 0 0 0 0 0 1 0 0 0 2*x + 3]
1662
+ [ 0 0 0 0 0 0 1 0 0 x]
1663
+ [ 0 0 0 0 0 0 0 1 0 2*x + 2]
1664
+ [ 0 0 0 0 0 0 0 0 1 0]
1665
+ [ 0 0 0 0 0 0 0 0 0 0]
1666
+ [ 0 0 0 0 0 0 0 0 0 0]
1667
+
1668
+ A semi-echelon form can be produced by invoking the single-underscore
1669
+ method directly::
1670
+
1671
+ sage: N = copy(M)
1672
+ sage: N._echelon_in_place_classical(reduced=False)
1673
+ (2, 1, 3, 4, 5, 6, 7, 8)
1674
+ sage: N
1675
+ [ 0 0 x 0 3*x + 2 0 0 0 2*x 0]
1676
+ [ 0 x + 3 0 0 0 3*x x + 4 0 0 0]
1677
+ [ 0 0 0 2*x 0 4*x + 1 4 0 0 0]
1678
+ [ 0 0 0 0 x + 4 x + 1 0 0 0 0]
1679
+ [ 0 0 0 0 0 3*x 0 x + 3 0 0]
1680
+ [ 0 0 0 0 0 0 2*x + 2 4*x 3*x + 3 0]
1681
+ [ 0 0 0 0 0 0 0 x + 1 1 x + 3]
1682
+ [ 0 0 0 0 0 0 0 0 4*x + 2 0]
1683
+ [ 0 0 0 0 0 0 0 0 0 0]
1684
+ [ 0 0 0 0 0 0 0 0 0 0]
1685
+
1686
+ TESTS:
1687
+
1688
+ We verify that the above echelon form is consistent with Sage's generic
1689
+ implementation of dense matrices::
1690
+
1691
+ sage: type(M)
1692
+ <class 'sage.matrix.matrix_gfpn_dense.Matrix_gfpn_dense'>
1693
+ sage: MS = MatrixSpace(M.base_ring(), M.nrows(), M.ncols(), implementation='generic')
1694
+ sage: X = MS(M)
1695
+ sage: type(X)
1696
+ <class 'sage.matrix.matrix_generic_dense.Matrix_generic_dense'>
1697
+ sage: X.echelon_form()
1698
+ [ 0 1 0 0 0 0 0 0 0 4*x + 4]
1699
+ [ 0 0 1 0 0 0 0 0 0 4*x + 2]
1700
+ [ 0 0 0 1 0 0 0 0 0 3*x + 4]
1701
+ [ 0 0 0 0 1 0 0 0 0 3*x + 3]
1702
+ [ 0 0 0 0 0 1 0 0 0 2*x + 3]
1703
+ [ 0 0 0 0 0 0 1 0 0 x]
1704
+ [ 0 0 0 0 0 0 0 1 0 2*x + 2]
1705
+ [ 0 0 0 0 0 0 0 0 1 0]
1706
+ [ 0 0 0 0 0 0 0 0 0 0]
1707
+ [ 0 0 0 0 0 0 0 0 0 0]
1708
+
1709
+ The following was a problem in a preliminary version of the code::
1710
+
1711
+ sage: K.<a> = GF(25)
1712
+ sage: M = MatrixSpace(K, 2, 4)([4, 4, 1, 0, 0, 2*a+1, a+2, 1])
1713
+ sage: M
1714
+ [ 4 4 1 0]
1715
+ [ 0 2*a + 1 a + 2 1]
1716
+ sage: M.echelonize()
1717
+ sage: M
1718
+ [ 1 0 3*a + 4 2*a + 2]
1719
+ [ 0 1 2*a 3*a + 3]
1720
+ """
1721
+ if self._nrows == 0 or self._ncols == 0:
1722
+ self.cache('in_echelon_form',True)
1723
+ self.cache('rank', 0)
1724
+ self.cache('pivots', ())
1725
+ return ()
1726
+ self._cache = None
1727
+ sig_on()
1728
+ try:
1729
+ MatEchelonize(self.Data)
1730
+ finally:
1731
+ sig_off()
1732
+ # Now, self.Data is in semi-echelon form.
1733
+ r = self.Data.Nor
1734
+ cdef size_t i, j, pos
1735
+ cdef PTR old, dest, src
1736
+ cdef FEL piv
1737
+ self.cache('rank', r)
1738
+ # Next, we do permutations to achieve the reduced echelon form,
1739
+ # if requested.
1740
+ if reduced:
1741
+ pivs = [(self.Data.PivotTable[i],i) for i in range(r)]
1742
+ pivs.sort()
1743
+ if pivs != [(self.Data.PivotTable[i],i) for i in range(r)] or self.Data.Nor < self._nrows:
1744
+ # We copy the row one by one, sorting their pivot positions
1745
+ old = self.Data.Data
1746
+ self.Data.Data = FfAlloc(self._nrows)
1747
+ for i, (pos,j) in enumerate(pivs):
1748
+ # We have to move row j to row i
1749
+ dest = self.Data.Data+FfCurrentRowSize*i
1750
+ memcpy(dest, old+FfCurrentRowSize*j, FfCurrentRowSize)
1751
+ self.Data.PivotTable[i] = pos
1752
+ sig_check()
1753
+ sig_free(old)
1754
+ self.Data.Nor = self._nrows
1755
+ # Now, the pivot columns are strictly increasing.
1756
+ # We now normalize each row, and annulate everything
1757
+ # above the pivot (currently, we only know that the matrix
1758
+ # is zero below the pivots).
1759
+ for i in range(r):
1760
+ src = MatGetPtr(self.Data, i)
1761
+ piv = FfExtract(src, self.Data.PivotTable[i])
1762
+ assert piv!=FF_ZERO
1763
+ if piv != FF_ONE:
1764
+ FfMulRow(src, mtx_tmultinv[piv])
1765
+ sig_check()
1766
+ for j in range(i):
1767
+ dest = MatGetPtr(self.Data, j)
1768
+ piv = FfExtract(dest, self.Data.PivotTable[i])
1769
+ if piv != FF_ONE:
1770
+ FfAddMulRow(dest, src, mtx_taddinv[piv])
1771
+ else:
1772
+ FfSubRow(dest, src)
1773
+ sig_check()
1774
+ elif self.Data.Nor < self._nrows:
1775
+ # Some rows may have vanished. In SageMath, we
1776
+ # want that the number of rows does not change,
1777
+ # thus, we have to append zero rows.
1778
+ self.Data.Data = <PTR>check_realloc(self.Data.Data, FfCurrentRowSize*self._nrows)
1779
+ memset(self.Data.Data + FfCurrentRowSize*self.Data.Nor, FF_ZERO, FfCurrentRowSize*(self._nrows-self.Data.Nor))
1780
+ self.Data.Nor = self._nrows
1781
+ self.cache('pivots', tuple(self.Data.PivotTable[i] for i in range(r)))
1782
+ self.cache('in_echelon_form',True)
1783
+ return self._cache['pivots']
1784
+
1785
+
1786
+ from sage.misc.superseded import deprecation_cython as deprecation
1787
+
1788
+
1789
+ def mtx_unpickle(f, int nr, int nc, data, bint m):
1790
+ r"""
1791
+ Helper function for unpickling.
1792
+
1793
+ EXAMPLES::
1794
+
1795
+ sage: K.<x> = GF(9)
1796
+ sage: M = MatrixSpace(K,10,10).random_element()
1797
+ sage: M == loads(dumps(M)) # indirect doctest
1798
+ True
1799
+ sage: M is loads(dumps(M))
1800
+ False
1801
+
1802
+ We also test pickles with zero rows and columns, as they may constitute
1803
+ corner cases. Note that in the following case, if ``sizeof(long)==8``,
1804
+ two matrix entries are stored in one byte, and therefore the last byte of
1805
+ a row is only half filled::
1806
+
1807
+ sage: M = matrix(K,3,5, [x, 0, 1, 0,-1, 0, 0, 0, 0, 0, -1, 0, 1, 0, x])
1808
+ sage: loads(dumps(M)) == M
1809
+ True
1810
+ sage: M = matrix(K,3,5, [0, 1, 0,-1, 0, x, 0, 1, 0, -1, 0, 0, 0, 0, 0])
1811
+ sage: loads(dumps(M)) == M
1812
+ True
1813
+
1814
+ TESTS:
1815
+
1816
+ We test that a pickle created by one machine can be understood
1817
+ by other machines with different architecture (see :issue:`23411`).
1818
+ Internally, a row is stored in a memory block of length a multiple
1819
+ of ``sizeof(long)``, which may be machine dependent, but in a pickle,
1820
+ only the bytes actually containing data of the row are stored, which
1821
+ is machine independent. We chose a matrix over the field with `13` elements.
1822
+ Since `13^2<255<13^3`, two columns will be stored in one byte. Our matrix
1823
+ has five columns, thus, one row will occupy three bytes in the pickle,
1824
+ but eight bytes (if ``sizeof(long)==8``) in memory, and the pickle
1825
+ string will be six bytes, since we have two rows::
1826
+
1827
+ sage: s = b'Uq\x82\xa7\x8bh'
1828
+ sage: len(s)
1829
+ 6
1830
+ sage: from sage.matrix.matrix_gfpn_dense import mtx_unpickle, Matrix_gfpn_dense
1831
+ sage: MS = MatrixSpace(GF(13), 2, 5, implementation=Matrix_gfpn_dense)
1832
+ sage: N = mtx_unpickle(MS, 2, 5, s, True)
1833
+ sage: N
1834
+ [ 6 7 8 9 10]
1835
+ [12 11 10 9 8]
1836
+ sage: type(N)
1837
+ <class 'sage.matrix.matrix_gfpn_dense.Matrix_gfpn_dense'>
1838
+
1839
+ We demonstrate that a slightly different pickle format can be understood
1840
+ as well, that was at some point used by some optional package::
1841
+
1842
+ sage: N == mtx_unpickle(int(13), 2, 5, s, True)
1843
+ True
1844
+
1845
+ In a previous version of this optional module, the whole memory chunk
1846
+ used to store the matrix was stored. The result would have been, as
1847
+ in the following example, a string of length 16. Unpickling works, but
1848
+ results in a warning::
1849
+
1850
+ sage: t = b'Uq\x82\x00\x00\x00\x00\x00\xa7\x8bh\x00\x00\x00\x00\x00'
1851
+ sage: len(t)
1852
+ 16
1853
+ sage: N == mtx_unpickle(MS, 2, 5, t, True)
1854
+ doctest:warning
1855
+ ...
1856
+ DeprecationWarning: Reading this pickle may be machine dependent
1857
+ See https://github.com/sagemath/sage/issues/23411 for details.
1858
+ True
1859
+
1860
+ Unpickling would even work in the case that the machine creating
1861
+ the deprecated pickle had ``sizeof(long)==9``::
1862
+
1863
+ sage: t = b'Uq\x82\x00\x00\x00\x00\x00\x00\xa7\x8bh\x00\x00\x00\x00\x00\x00'
1864
+ sage: len(t)
1865
+ 18
1866
+ sage: N == mtx_unpickle(MS, 2, 5, t, True)
1867
+ True
1868
+
1869
+ The data may be empty, which results in the zero matrix::
1870
+
1871
+ sage: mtx_unpickle(MS, 2, 5, b'', True)
1872
+ [0 0 0 0 0]
1873
+ [0 0 0 0 0]
1874
+
1875
+ We test further corner cases. A :exc:`ValueError` is raised if the number
1876
+ of bytes in the pickle does not comply with either the old or the new
1877
+ pickle format (we test several code paths here)::
1878
+
1879
+ sage: t = b'Uq\x82\x00\x00\x00\x00\x00\xa7\x8bh\x00\x00\x00\x00\x00\x00'
1880
+ sage: mtx_unpickle(MS, 2, 5, t, True)
1881
+ Traceback (most recent call last):
1882
+ ...
1883
+ ValueError: Expected a pickle with 3*2 bytes, got 17 instead
1884
+ sage: t = b'Uq\x82\x00\x00\x00\x00\x00\xa7\x8bh\x00\x00\x00\x00\x00\x00'
1885
+ sage: mtx_unpickle(MS, 2, 5, t[:4], True)
1886
+ Traceback (most recent call last):
1887
+ ...
1888
+ ValueError: Expected a pickle with 3*2 bytes, got 2*2 instead
1889
+ sage: MS = MatrixSpace(GF(13), 0, 5)
1890
+ sage: mtx_unpickle(MS, 0, 5, s, True)
1891
+ Traceback (most recent call last):
1892
+ ...
1893
+ ValueError: This matrix pickle contains data, thus, the number of rows
1894
+ and columns must be positive
1895
+ sage: MS = MatrixSpace(GF(13), 3, 5)
1896
+ sage: mtx_unpickle(MS, 2, 5, s, True)
1897
+ Traceback (most recent call last):
1898
+ ...
1899
+ ValueError: Inconsistent dimensions in this matrix pickle
1900
+ sage: mtx_unpickle(MatrixSpace(GF(19),0,5), 0, 5, b'', True)
1901
+ []
1902
+ """
1903
+ # The expected input type is bytes. However, Python-2 legacy pickles do
1904
+ # not distinguish between str and bytes. If such pickle is unpickled
1905
+ # in Python-3, Sage will receive a str in `latin1` encoding. Therefore,
1906
+ # in the following line, we use a helper function that would return bytes,
1907
+ # regardless whether the input is bytes or str.
1908
+ if isinstance(data, str):
1909
+ data = data.encode('latin1')
1910
+ cdef bytes Data = data
1911
+ if isinstance(f, int):
1912
+ # This is for old pickles created with the group cohomology spkg
1913
+ MS = MatrixSpace(GF(f, 'z'), nr, nc, implementation=Matrix_gfpn_dense)
1914
+ else:
1915
+ MS = f
1916
+ if MS.nrows() != nr or MS.ncols() != nc:
1917
+ raise ValueError("Inconsistent dimensions in this matrix pickle")
1918
+ f = MS.base_ring().order()
1919
+
1920
+ OUT = <Matrix_gfpn_dense>Matrix_gfpn_dense.__new__(Matrix_gfpn_dense, MS)
1921
+ sig_on()
1922
+ try:
1923
+ OUT.Data = MatAlloc(f, nr, nc)
1924
+ finally:
1925
+ sig_off()
1926
+ OUT._is_immutable = not m
1927
+ OUT._converter = FieldConverter(OUT._base_ring)
1928
+ cdef char *x
1929
+ cdef PTR pt
1930
+ cdef size_t lenData = len(Data)
1931
+ cdef size_t pickled_rowsize
1932
+ cdef size_t i
1933
+ if Data:
1934
+ if nr <= 0 or nc <= 0:
1935
+ raise ValueError("This matrix pickle contains data, thus, the number of rows and columns must be positive")
1936
+ pickled_rowsize = lenData//nr
1937
+ if lenData != pickled_rowsize*nr:
1938
+ raise ValueError(f"Expected a pickle with {FfCurrentRowSizeIo}*{nr} bytes, got {lenData} instead")
1939
+ x = PyBytes_AsString(Data)
1940
+ if pickled_rowsize == FfCurrentRowSizeIo:
1941
+ pt = OUT.Data.Data
1942
+ for i in range(nr):
1943
+ memcpy(pt,x,FfCurrentRowSizeIo)
1944
+ x += FfCurrentRowSizeIo
1945
+ FfStepPtr(&(pt))
1946
+ sig_check()
1947
+ elif pickled_rowsize >= FfCurrentRowSizeIo:
1948
+ deprecation(23411, "Reading this pickle may be machine dependent")
1949
+ if pickled_rowsize == FfCurrentRowSize:
1950
+ memcpy(OUT.Data.Data, x, OUT.Data.RowSize*OUT.Data.Nor)
1951
+ else:
1952
+ pt = OUT.Data.Data
1953
+ for i in range(nr):
1954
+ memcpy(pt,x,FfCurrentRowSizeIo)
1955
+ x += pickled_rowsize
1956
+ FfStepPtr(&(pt))
1957
+ sig_check()
1958
+ else:
1959
+ raise ValueError(f"Expected a pickle with {FfCurrentRowSizeIo}*{nr} bytes, got {pickled_rowsize}*{nr} instead")
1960
+ return OUT