Dense matrices over \(\GF{2^e}\) for \(2 \leq e \leq 16\) using the M4RIE library - Matrices and Spaces of Matrices

Skip to content
Back to top
View this page
Edit this page

# Dense matrices over \(\GF{2^e}\) for \(2 \leq e \leq 16\) using the M4RIE library

The M4RIE library offers two matrix representations:

1. `mzed_t`

> m x n matrices over \(\GF{2^e}\) are internally represented roughly as m x (en) matrices over \(\GF{2}\). Several elements are packed into words such that each element is filled with zeroes until the next power of two. Thus, for example, elements of \(\GF{2^3}\) are represented as `[0xxx|0xxx|0xxx|0xxx|...]`. This representation is wrapped as `Matrix_gf2e_dense` in Sage.

> Multiplication and elimination both use “Newton-John” tables. These tables are simply all possible multiples of a given row in a matrix such that a scale+add operation is reduced to a table lookup + add. On top of Newton-John multiplication M4RIE implements asymptotically fast Strassen-Winograd multiplication. Elimination uses simple Gaussian elimination which requires \(O(n^3)\) additions but only \(O(n^2 * 2^e)\) multiplications.

2. `mzd_slice_t`

> m x n matrices over \(\GF{2^e}\) are internally represented as slices of m x n matrices over \(\GF{2}\). This representation allows for very fast matrix times matrix products using Karatsuba’s polynomial multiplication for polynomials over matrices. However, it is not feature complete yet and hence not wrapped in Sage for now.

See http://m4ri.sagemath.org for more details on the M4RIE library.

EXAMPLES:

Sage

    sage: K.<a> = GF(2^8)
    sage: A = random_matrix(K, 3,4)
    sage: E = A.echelon_form()
    sage: A.row_space() == E.row_space()
    True
    sage: all(r[r.nonzero_positions()[0]] == 1 for r in E.rows() if r)
    True

Python

    >>> from sage.all import *
    >>> K = GF(Integer(2)**Integer(8), names=('a',)); (a,) = K._first_ngens(1)
    >>> A = random_matrix(K, Integer(3),Integer(4))
    >>> E = A.echelon_form()
    >>> A.row_space() == E.row_space()
    True
    >>> all(r[r.nonzero_positions()[Integer(0)]] == Integer(1) for r in E.rows() if r)
    True

AUTHOR:

- Martin Albrecht <martinralbrecht@googlemail.com>

Todo

Wrap `mzd_slice_t`.

REFERENCES:

- [BB2009]

class sage.matrix.matrix_gf2e_dense.M4RIE_finite_field

  Bases: `object`

  A thin wrapper around the M4RIE finite field class such that we can put it in a hash table. This class is not meant for public consumption.

class sage.matrix.matrix_gf2e_dense.Matrix_gf2e_dense

  Bases: `Matrix_dense`

  Create new matrix over \(GF(2^e)\) for \(2 \leq e \leq 16\).

  INPUT:

  - `parent` – a matrix space over `GF(2^e)`

  - `entries` – see `matrix()`

  - `copy` – ignored (for backwards compatibility)

  - `coerce` – if `False`, assume without checking that the entries lie in the base ring

  EXAMPLES:

  Sage

      sage: K.<a> = GF(2^4)
      sage: l = [K.random_element() for _ in range(3*4)]

      sage: A = Matrix(K, 3, 4, l)
      sage: l == A.list()
      True

      sage: l[0] == A[0,0]
      True

      sage: A = Matrix(K, 3, 3, a); A
      [a 0 0]
      [0 a 0]
      [0 0 a]

  Python

      >>> from sage.all import *
      >>> K = GF(Integer(2)**Integer(4), names=('a',)); (a,) = K._first_ngens(1)
      >>> l = [K.random_element() for _ in range(Integer(3)*Integer(4))]

      >>> A = Matrix(K, Integer(3), Integer(4), l)
      >>> l == A.list()
      True

      >>> l[Integer(0)] == A[Integer(0),Integer(0)]
      True

      >>> A = Matrix(K, Integer(3), Integer(3), a); A
      [a 0 0]
      [0 a 0]
      [0 0 a]

  augment(right)

    Augments `self` with `right`.

    INPUT:

    - `right` – a matrix

    EXAMPLES:

    Sage

        sage: K.<a> = GF(2^4)
        sage: MS = MatrixSpace(K,3,3)
        sage: A = random_matrix(K,3,3)
        sage: B = A.augment(MS(1))
        sage: B.echelonize()
        sage: C = B.matrix_from_columns([3,4,5])
        sage: A.rank() < 3 or C == ~A
        True
        sage: A.rank() < 3 or C*A == MS(1)
        True

    Python

        >>> from sage.all import *
        >>> K = GF(Integer(2)**Integer(4), names=('a',)); (a,) = K._first_ngens(1)
        >>> MS = MatrixSpace(K,Integer(3),Integer(3))
        >>> A = random_matrix(K,Integer(3),Integer(3))
        >>> B = A.augment(MS(Integer(1)))
        >>> B.echelonize()
        >>> C = B.matrix_from_columns([Integer(3),Integer(4),Integer(5)])
        >>> A.rank() < Integer(3) or C == ~A
        True
        >>> A.rank() < Integer(3) or C*A == MS(Integer(1))
        True

  cling(*C)

    Pack the matrices over \(\GF{2}\) into this matrix over \(\GF{2^e}\).

    Elements in \(\GF{2^e}\) can be represented as \(\sum c_i a^i\) where \(a\) is a root the minimal polynomial. If this matrix is \(A\) then this function writes \(c_i a^i\) to the entry \(A[x,y]\) where \(c_i\) is the entry \(C_i[x,y]\).

    INPUT:

    - `C` – list of matrices over GF(2)

    EXAMPLES:

    Sage

        sage: K.<a> = GF(2^2)
        sage: A = matrix(K, 5, 5)
        sage: A0 = random_matrix(GF(2), 5, 5)
        sage: A1 = random_matrix(GF(2), 5, 5)
        sage: A.cling(A0, A1)
        sage: all(A.list()[i] == A0.list()[i] + a*A1.list()[i] for i in range(25))
        True

    Python

        >>> from sage.all import *
        >>> K = GF(Integer(2)**Integer(2), names=('a',)); (a,) = K._first_ngens(1)
        >>> A = matrix(K, Integer(5), Integer(5))
        >>> A0 = random_matrix(GF(Integer(2)), Integer(5), Integer(5))
        >>> A1 = random_matrix(GF(Integer(2)), Integer(5), Integer(5))
        >>> A.cling(A0, A1)
        >>> all(A.list()[i] == A0.list()[i] + a*A1.list()[i] for i in range(Integer(25)))
        True

    Slicing and clinging are inverse operations:

    Sage

        sage: B0, B1 = A.slice()
        sage: B0 == A0 and B1 == A1
        True

    [Python]

        >>> from sage.all import *
        >>> B0, B1 = A.slice()
        >>> B0 == A0 and B1 == A1
        True

  determinant()

    Return the determinant of this matrix.

    Relies directly on M4RIE’s PLE decomposition, and incidentally caches the rank of `self`.

    EXAMPLES:

    Sage

        sage: gf4.<z> = GF(4)
        sage: mat = matrix(gf4, 2, 2, [[z + 1, z + 1], [z, 1]])
        sage: mat
        [z + 1 z + 1]
        [    z     1]
        sage: mat.determinant()
        z
        sage: gf256.<t> = GF(2**8)
        sage: mat = matrix(gf256, 3, 3, [[1, t, t**2],
        ....:                            [t**2, 1, t],
        ....:                            [t, t**2, 1]])
        sage: mat.determinant()
        t^6 + 1
        sage: mat = matrix(gf256, 3, 3, [[1, t, t**2],
        ....:                            [t**2, 1, t],
        ....:                            [t**2 + 1, t + 1, t**2 + t]])
        sage: mat.determinant()
        0

    Python

        >>> from sage.all import *
        >>> gf4 = GF(Integer(4), names=('z',)); (z,) = gf4._first_ngens(1)
        >>> mat = matrix(gf4, Integer(2), Integer(2), [[z + Integer(1), z + Integer(1)], [z, Integer(1)]])
        >>> mat
        [z + 1 z + 1]
        [    z     1]
        >>> mat.determinant()
        z
        >>> gf256 = GF(Integer(2)**Integer(8), names=('t',)); (t,) = gf256._first_ngens(1)
        >>> mat = matrix(gf256, Integer(3), Integer(3), [[Integer(1), t, t**Integer(2)],
        ...                            [t**Integer(2), Integer(1), t],
        ...                            [t, t**Integer(2), Integer(1)]])
        >>> mat.determinant()
        t^6 + 1
        >>> mat = matrix(gf256, Integer(3), Integer(3), [[Integer(1), t, t**Integer(2)],
        ...                            [t**Integer(2), Integer(1), t],
        ...                            [t**Integer(2) + Integer(1), t + Integer(1), t**Integer(2) + t]])
        >>> mat.determinant()
        0

    Non-square matrices and the \(0 imes 0\) matrix are taken care of:

    Sage

        sage: matrix(gf4, 0, 0).determinant()
        1
        sage: matrix(gf4, 3, 2).determinant()
        Traceback (most recent call last):
        ...
        ValueError: self must be a square matrix

    [Python]

        >>> from sage.all import *
        >>> matrix(gf4, Integer(0), Integer(0)).determinant()
        1
        >>> matrix(gf4, Integer(3), Integer(2)).determinant()
        Traceback (most recent call last):
        ...
        ValueError: self must be a square matrix

  echelonize(algorithm='heuristic', reduced=True, **kwds)

    Compute the row echelon form of `self` in place.

    INPUT:

    - `algorithm` – one of the following - `heuristic` – let M4RIE decide (default) - `newton_john` – use newton_john table based algorithm - `ple` – use PLE decomposition - `naive` – use naive cubic Gaussian elimination (M4RIE implementation) - `builtin` – use naive cubic Gaussian elimination (Sage implementation)

    - `reduced` – if `True` return reduced echelon form. No guarantee is given that the matrix is not reduced if `False` (default: `True`)

    EXAMPLES:

    Sage

        sage: K.<a> = GF(2^4)
        sage: m,n  = 3, 5
        sage: A = random_matrix(K, 3, 5)
        sage: R = A.row_space()
        sage: A.echelonize()
        sage: all(r[r.nonzero_positions()[0]] == 1 for r in A.rows() if r)
        True
        sage: A.row_space() == R
        True

        sage: K.<a> = GF(2^3)
        sage: m,n  = 3, 5
        sage: MS = MatrixSpace(K,m,n)
        sage: A = random_matrix(K, 3, 5)
        sage: B = copy(A).echelon_form('newton_john')
        sage: C = copy(A).echelon_form('naive')
        sage: D = copy(A).echelon_form('builtin')
        sage: B == C == D
        True
        sage: all(r[r.nonzero_positions()[0]] == 1 for r in B.rows() if r)
        True

    Python

        >>> from sage.all import *
        >>> K = GF(Integer(2)**Integer(4), names=('a',)); (a,) = K._first_ngens(1)
        >>> m,n  = Integer(3), Integer(5)
        >>> A = random_matrix(K, Integer(3), Integer(5))
        >>> R = A.row_space()
        >>> A.echelonize()
        >>> all(r[r.nonzero_positions()[Integer(0)]] == Integer(1) for r in A.rows() if r)
        True
        >>> A.row_space() == R
        True

        >>> K = GF(Integer(2)**Integer(3), names=('a',)); (a,) = K._first_ngens(1)
        >>> m,n  = Integer(3), Integer(5)
        >>> MS = MatrixSpace(K,m,n)
        >>> A = random_matrix(K, Integer(3), Integer(5))
        >>> B = copy(A).echelon_form('newton_john')
        >>> C = copy(A).echelon_form('naive')
        >>> D = copy(A).echelon_form('builtin')
        >>> B == C == D
        True
        >>> all(r[r.nonzero_positions()[Integer(0)]] == Integer(1) for r in B.rows() if r)
        True

  is_invertible()

    Return `True` if this matrix is invertible.

    EXAMPLES:

    Sage

        sage: m = Matrix(GL(2^6, GF(2^6)).random_element())
        sage: m.is_invertible()
        True

    Python

        >>> from sage.all import *
        >>> m = Matrix(GL(Integer(2)**Integer(6), GF(Integer(2)**Integer(6))).random_element())
        >>> m.is_invertible()
        True

  randomize(density=1, nonzero=False, *args, **kwds)

    Randomize `density` proportion of the entries of this matrix, leaving the rest unchanged.

    INPUT:

    - `density` – float; proportion (roughly) to be considered for changes

    - `nonzero` – boolean (default: `False`); whether the new entries are forced to be nonzero

    OUTPUT: none, the matrix is modified in-place

    EXAMPLES:

    Sage

        sage: K.<a> = GF(2^4)
        sage: total_count = 0
        sage: from collections import defaultdict
        sage: dic = defaultdict(Integer)
        sage: def add_samples():
        ....:     global dic, total_count
        ....:     for _ in range(100):
        ....:         A = Matrix(K,3,3)
        ....:         A.randomize()
        ....:         for a in A.list():
        ....:             dic[a] += 1
        ....:             total_count += 1.0
        sage: add_samples()
        sage: while not all(abs(dic[a]/total_count - 1/16) < 0.01 for a in dic):
        ....:     add_samples()

        sage: def add_sample(density):
        ....:     global density_sum, total_count
        ....:     total_count += 1.0
        ....:     density_sum += random_matrix(K, 1000, 1000, density=density).density()

        sage: density_sum = 0.0
        sage: total_count = 0.0
        sage: add_sample(0.1)
        sage: while abs(density_sum/total_count - 0.1) > 0.001:
        ....:     add_sample(0.1)

        sage: density_sum = 0.0
        sage: total_count = 0.0
        sage: add_sample(1.0)
        sage: while abs(density_sum/total_count - 1.0) > 0.001:
        ....:     add_sample(1.0)

        sage: density_sum = 0.0
        sage: total_count = 0.0
        sage: add_sample(0.5)
        sage: while abs(density_sum/total_count - 0.5) > 0.001:
        ....:     add_sample(0.5)

    Python

        >>> from sage.all import *
        >>> K = GF(Integer(2)**Integer(4), names=('a',)); (a,) = K._first_ngens(1)
        >>> total_count = Integer(0)
        >>> from collections import defaultdict
        >>> dic = defaultdict(Integer)
        >>> def add_samples():
        ...     global dic, total_count
        ...     for _ in range(Integer(100)):
        ...         A = Matrix(K,Integer(3),Integer(3))
        ...         A.randomize()
        ...         for a in A.list():
        ...             dic[a] += Integer(1)
        ...             total_count += RealNumber('1.0')
        >>> add_samples()
        >>> while not all(abs(dic[a]/total_count - Integer(1)/Integer(16)) < RealNumber('0.01') for a in dic):
        ...     add_samples()

        >>> def add_sample(density):
        ...     global density_sum, total_count
        ...     total_count += RealNumber('1.0')
        ...     density_sum += random_matrix(K, Integer(1000), Integer(1000), density=density).density()

        >>> density_sum = RealNumber('0.0')
        >>> total_count = RealNumber('0.0')
        >>> add_sample(RealNumber('0.1'))
        >>> while abs(density_sum/total_count - RealNumber('0.1')) > RealNumber('0.001'):
        ...     add_sample(RealNumber('0.1'))

        >>> density_sum = RealNumber('0.0')
        >>> total_count = RealNumber('0.0')
        >>> add_sample(RealNumber('1.0'))
        >>> while abs(density_sum/total_count - RealNumber('1.0')) > RealNumber('0.001'):
        ...     add_sample(RealNumber('1.0'))

        >>> density_sum = RealNumber('0.0')
        >>> total_count = RealNumber('0.0')
        >>> add_sample(RealNumber('0.5'))
        >>> while abs(density_sum/total_count - RealNumber('0.5')) > RealNumber('0.001'):
        ...     add_sample(RealNumber('0.5'))

    Note, that the matrix is updated and not zero-ed out before being randomized:

    Sage

        sage: def add_sample(density, nonzero):
        ....:     global density_sum, total_count
        ....:     total_count += 1.0
        ....:     A = matrix(K, 1000, 1000)
        ....:     A.randomize(nonzero=nonzero, density=density)
        ....:     A.randomize(nonzero=nonzero, density=density)
        ....:     density_sum += A.density()

        sage: density_sum = 0.0
        sage: total_count = 0.0
        sage: add_sample(0.1, True)
        sage: while abs(density_sum/total_count - (1 - 0.9^2)) > 0.001:
        ....:     add_sample(0.1, True)

        sage: density_sum = 0.0
        sage: total_count = 0.0
        sage: add_sample(0.1, False)
        sage: while abs(density_sum/total_count - (1 - 0.9^2)*15/16) > 0.001:
        ....:     add_sample(0.1, False)

        sage: density_sum = 0.0
        sage: total_count = 0.0
        sage: add_sample(0.05, True)
        sage: while abs(density_sum/total_count - (1 - 0.95^2)) > 0.001:
        ....:     add_sample(0.05, True)

        sage: density_sum = 0.0
        sage: total_count = 0.0
        sage: add_sample(0.5, True)
        sage: while abs(density_sum/total_count - (1 - 0.5^2)) > 0.001:
        ....:     add_sample(0.5, True)

    [Python]

        >>> from sage.all import *
        >>> def add_sample(density, nonzero):
        ...     global density_sum, total_count
        ...     total_count += RealNumber('1.0')
        ...     A = matrix(K, Integer(1000), Integer(1000))
        ...     A.randomize(nonzero=nonzero, density=density)
        ...     A.randomize(nonzero=nonzero, density=density)
        ...     density_sum += A.density()

        >>> density_sum = RealNumber('0.0')
        >>> total_count = RealNumber('0.0')
        >>> add_sample(RealNumber('0.1'), True)
        >>> while abs(density_sum/total_count - (Integer(1) - RealNumber('0.9')**Integer(2))) > RealNumber('0.001'):
        ...     add_sample(RealNumber('0.1'), True)

        >>> density_sum = RealNumber('0.0')
        >>> total_count = RealNumber('0.0')
        >>> add_sample(RealNumber('0.1'), False)
        >>> while abs(density_sum/total_count - (Integer(1) - RealNumber('0.9')**Integer(2))*Integer(15)/Integer(16)) > RealNumber('0.001'):
        ...     add_sample(RealNumber('0.1'), False)

        >>> density_sum = RealNumber('0.0')
        >>> total_count = RealNumber('0.0')
        >>> add_sample(RealNumber('0.05'), True)
        >>> while abs(density_sum/total_count - (Integer(1) - RealNumber('0.95')**Integer(2))) > RealNumber('0.001'):
        ...     add_sample(RealNumber('0.05'), True)

        >>> density_sum = RealNumber('0.0')
        >>> total_count = RealNumber('0.0')
        >>> add_sample(RealNumber('0.5'), True)
        >>> while abs(density_sum/total_count - (Integer(1) - RealNumber('0.5')**Integer(2))) > RealNumber('0.001'):
        ...     add_sample(RealNumber('0.5'), True)

  rank()

    Return the rank of this matrix (cached).

    EXAMPLES:

    Sage

        sage: K.<a> = GF(2^4)
        sage: A = random_matrix(K, 10, 10, algorithm='unimodular')
        sage: A.rank()
        10
        sage: A = matrix(K, 10, 0)
        sage: A.rank()
        0

    Python

        >>> from sage.all import *
        >>> K = GF(Integer(2)**Integer(4), names=('a',)); (a,) = K._first_ngens(1)
        >>> A = random_matrix(K, Integer(10), Integer(10), algorithm='unimodular')
        >>> A.rank()
        10
        >>> A = matrix(K, Integer(10), Integer(0))
        >>> A.rank()
        0

  slice()

    Unpack this matrix into matrices over \(\GF{2}\).

    Elements in \(\GF{2^e}\) can be represented as \(\sum c_i a^i\) where \(a\) is a root the minimal polynomial. This function returns a tuple of matrices \(C\) whose entry \(C_i[x,y]\) is the coefficient of \(c_i\) in \(A[x,y]\) if this matrix is \(A\).

    EXAMPLES:

    Sage

        sage: K.<a> = GF(2^2)
        sage: A = random_matrix(K, 5, 5)
        sage: A0, A1 = A.slice()
        sage: all(A.list()[i] == A0.list()[i] + a*A1.list()[i] for i in range(25))
        True

        sage: K.<a> = GF(2^3)
        sage: A = random_matrix(K, 5, 5)
        sage: A0, A1, A2 = A.slice()
        sage: all(A.list()[i] == A0.list()[i] + a*A1.list()[i] + a^2*A2.list()[i] for i in range(25))
        True

    Python

        >>> from sage.all import *
        >>> K = GF(Integer(2)**Integer(2), names=('a',)); (a,) = K._first_ngens(1)
        >>> A = random_matrix(K, Integer(5), Integer(5))
        >>> A0, A1 = A.slice()
        >>> all(A.list()[i] == A0.list()[i] + a*A1.list()[i] for i in range(Integer(25)))
        True

        >>> K = GF(Integer(2)**Integer(3), names=('a',)); (a,) = K._first_ngens(1)
        >>> A = random_matrix(K, Integer(5), Integer(5))
        >>> A0, A1, A2 = A.slice()
        >>> all(A.list()[i] == A0.list()[i] + a*A1.list()[i] + a**Integer(2)*A2.list()[i] for i in range(Integer(25)))
        True

    Slicing and clinging are inverse operations:

    Sage

        sage: B = matrix(K, 5, 5)
        sage: B.cling(A0, A1, A2)
        sage: B == A
        True

    [Python]

        >>> from sage.all import *
        >>> B = matrix(K, Integer(5), Integer(5))
        >>> B.cling(A0, A1, A2)
        >>> B == A
        True

  submatrix(row=0, col=0, nrows=-1, ncols=-1)

    Return submatrix from the index `row,col` (inclusive) with dimension `nrows x ncols`.

    INPUT:

    - `row` – index of start row

    - `col` – index of start column

    - `nrows` – number of rows of submatrix

    - `ncols` – number of columns of submatrix

    EXAMPLES:

    Sage

        sage: K.<a> = GF(2^10)
        sage: A = random_matrix(K,200,200)
        sage: A[0:2,0:2] == A.submatrix(0,0,2,2)
        True
        sage: A[0:100,0:100] == A.submatrix(0,0,100,100)
        True
        sage: A == A.submatrix(0,0,200,200)
        True

        sage: A[1:3,1:3] == A.submatrix(1,1,2,2)
        True
        sage: A[1:100,1:100] == A.submatrix(1,1,99,99)
        True
        sage: A[1:200,1:200] == A.submatrix(1,1,199,199)
        True

    Python

        >>> from sage.all import *
        >>> K = GF(Integer(2)**Integer(10), names=('a',)); (a,) = K._first_ngens(1)
        >>> A = random_matrix(K,Integer(200),Integer(200))
        >>> A[Integer(0):Integer(2),Integer(0):Integer(2)] == A.submatrix(Integer(0),Integer(0),Integer(2),Integer(2))
        True
        >>> A[Integer(0):Integer(100),Integer(0):Integer(100)] == A.submatrix(Integer(0),Integer(0),Integer(100),Integer(100))
        True
        >>> A == A.submatrix(Integer(0),Integer(0),Integer(200),Integer(200))
        True

        >>> A[Integer(1):Integer(3),Integer(1):Integer(3)] == A.submatrix(Integer(1),Integer(1),Integer(2),Integer(2))
        True
        >>> A[Integer(1):Integer(100),Integer(1):Integer(100)] == A.submatrix(Integer(1),Integer(1),Integer(99),Integer(99))
        True
        >>> A[Integer(1):Integer(200),Integer(1):Integer(200)] == A.submatrix(Integer(1),Integer(1),Integer(199),Integer(199))
        True

    TESTS for handling of default arguments (Issue #18761):

    Sage

        sage: A.submatrix(17,15) == A.submatrix(17,15,183,185)
        True
        sage: A.submatrix(row=100,col=37,nrows=1,ncols=3) == A.submatrix(100,37,1,3)
        True

    [Python]

        >>> from sage.all import *
        >>> A.submatrix(Integer(17),Integer(15)) == A.submatrix(Integer(17),Integer(15),Integer(183),Integer(185))
        True
        >>> A.submatrix(row=Integer(100),col=Integer(37),nrows=Integer(1),ncols=Integer(3)) == A.submatrix(Integer(100),Integer(37),Integer(1),Integer(3))
        True

sage.matrix.matrix_gf2e_dense.unpickle_matrix_gf2e_dense_v0(a, base_ring, nrows, ncols)

  EXAMPLES:

  Sage

      sage: K.<a> = GF(2^2)
      sage: A = random_matrix(K,10,10)
      sage: f, s= A.__reduce__()
      sage: from sage.matrix.matrix_gf2e_dense import unpickle_matrix_gf2e_dense_v0
      sage: f == unpickle_matrix_gf2e_dense_v0
      True
      sage: f(*s) == A
      True

  Python

      >>> from sage.all import *
      >>> K = GF(Integer(2)**Integer(2), names=('a',)); (a,) = K._first_ngens(1)
      >>> A = random_matrix(K,Integer(10),Integer(10))
      >>> f, s= A.__reduce__()
      >>> from sage.matrix.matrix_gf2e_dense import unpickle_matrix_gf2e_dense_v0
      >>> f == unpickle_matrix_gf2e_dense_v0
      True
      >>> f(*s) == A
      True

  We can still unpickle pickles from before Issue #19240:

  Sage

      sage: old_pickle = b'x\x9c\x85RKo\xd3@\x10\xae\xdd$$\xdb&\xe5U\x1e-\x8f\xc2\xc9\x12RD#$\xce\xa0\xb4\x80\x07\xa2\xca\xc2\x07\x0e\xd5\xe2:\x1b\xdb\x8acg\x1c\xa7J\x85*!\xa4\x90\xe6\x07p\xe0\xc4\x01q\xe5\xc4\x19\xf5\xd0?\xc1\x81\xdf\x80\xb8q\x0b\xb3\x8eMS\xa1\x82V;;\xb3\xdf\xce\xf7\xcd\x8e\xe6\xb5j\xf7,GT;V\x1cy\x83\xf4\xe0\x9d\xb0Y\x13\xbc)\x82\x9e`\xfd\xa0\xeb\xd9m_\xf0\xbf1\xbe{\x97\xa1\xa2\x9d\xc6\xf0\x0f\x82,\x7f\x9d\xa1\xaa\x81\n\xb9m\x9c\xd7\xf4\xf1d2\x81-h\xc0#(\x03\x83\x15\xdas\xc9*\xc3\x13x\x0cu0\xd28\x97\x9e*(0\x9f\xfa\x1b\xd0\xd2\x7fH\x82\xb5\xf4\xa2@TO\xe19\x01I\xac\x136\x991\x9f\xa4\xf9&\xcd\x07i\xbe\xcb\xd4ib\t\xba\xa4\xf6\x02zIT\xd1\x8f2(u\x15\xfd\x9d<\xee@\x05V\xd3\x94E*\xb0\x0e\x0fH\xad\xa8\xbf\x97\xa0\r\x03\xfd\xf0\xb8\x1aU\xff\x92\x90\xe8?\xa5\xd6\x814_\xa5\xf9(\xcd\xafc\xe99\xe2\xd9\xa0\x06\xd4\xf5\xcf\xf2\xf2!\xbc\xd4\xdf\x90#\xc0\x8f\r\xccM\x1b\xdd\x8b\xa3\xbe\x1d\xf7#QmYv\x1cF{\xcc\x11\x81\x88<\x9b\xa71\xcf:\xce0\xaf\x9d\x96\xe3\x87a\xbb\xdf\xe5\x8e\x1f\xeeX>\xc3\x82\xb9\xb0\xe9\x05^,6=\xe17\xf1\xcc\xd0\xc0"u\xb0d\xe6wDl\xdd\x1fa)e\x8a\xbc\xc0\xe9U\xbd \x16\x8e\x88X\xc7j\x0b\x9e\x05\xc8L\xe5\x1e%.\x98\x8a5\xc4\xc5\xd9\xf7\xdd\xd0\xdf\x0b\xc2\x8eg\xf93.wZ\xb5\xc1\x94B\xf8\xa2#\x82\x98a\xf9\xffY\x12\xe3v\x18L\xff\x14Fl\xeb\x0ff\x10\xc4\xb0\xa2\xb9y\xcd-\xba%\xcd\xa5\x8ajT\xd1\x92\xa9\x0c\x86x\xb6a\xe6h\xf8\x02<g\xaa\xaf\xf6\xdd%\x89\xae\x13z\xfe \xc6\x0b\xfb1^4p\x99\x1e6\xc6\xd4\xebK\xdbx\xf9\xc4\x8f[Iw\xf8\x89\xef\xcbQf\xcfh\xe3\x95\x8c\xebj&\xb9\xe2.\x8f\x0c\\ui\x89\xf1x\xf4\xd6\xc0kf\xc1\xf1v\xad(\xc4\xeb\x89~\xfa\xf0\x06\xa8\xa4\x7f\x93\xf4\xd7\x0c\xbcE#\xad\x92\xfc\xed\xeao\xefX\\\x03'
      sage: loads(old_pickle)
      [    0     a]
      [a + 1     1]

  [Python]

      >>> from sage.all import *
      >>> old_pickle = b'x\x9c\x85RKo\xd3@\x10\xae\xdd$$\xdb&\xe5U\x1e-\x8f\xc2\xc9\x12RD#$\xce\xa0\xb4\x80\x07\xa2\xca\xc2\x07\x0e\xd5\xe2:\x1b\xdb\x8acg\x1c\xa7J\x85*!\xa4\x90\xe6\x07p\xe0\xc4\x01q\xe5\xc4\x19\xf5\xd0?\xc1\x81\xdf\x80\xb8q\x0b\xb3\x8eMS\xa1\x82V;;\xb3\xdf\xce\xf7\xcd\x8e\xe6\xb5j\xf7,GT;V\x1cy\x83\xf4\xe0\x9d\xb0Y\x13\xbc)\x82\x9e`\xfd\xa0\xeb\xd9m_\xf0\xbf1\xbe{\x97\xa1\xa2\x9d\xc6\xf0\x0f\x82,\x7f\x9d\xa1\xaa\x81\n\xb9m\x9c\xd7\xf4\xf1d2\x81-h\xc0#(\x03\x83\x15\xdas\xc9*\xc3\x13x\x0cu0\xd28\x97\x9e*(0\x9f\xfa\x1b\xd0\xd2\x7fH\x82\xb5\xf4\xa2@TO\xe19\x01I\xac\x136\x991\x9f\xa4\xf9&\xcd\x07i\xbe\xcb\xd4ib\t\xba\xa4\xf6\x02zIT\xd1\x8f2(u\x15\xfd\x9d<\xee@\x05V\xd3\x94E*\xb0\x0e\x0fH\xad\xa8\xbf\x97\xa0\r\x03\xfd\xf0\xb8\x1aU\xff\x92\x90\xe8?\xa5\xd6\x814_\xa5\xf9(\xcd\xafc\xe99\xe2\xd9\xa0\x06\xd4\xf5\xcf\xf2\xf2!\xbc\xd4\xdf\x90#\xc0\x8f\r\xccM\x1b\xdd\x8b\xa3\xbe\x1d\xf7#QmYv\x1cF{\xcc\x11\x81\x88<\x9b\xa71\xcf:\xce0\xaf\x9d\x96\xe3\x87a\xbb\xdf\xe5\x8e\x1f\xeeX>\xc3\x82\xb9\xb0\xe9\x05^,6=\xe17\xf1\xcc\xd0\xc0"u\xb0d\xe6wDl\xdd\x1fa)e\x8a\xbc\xc0\xe9U\xbd \x16\x8e\x88X\xc7j\x0b\x9e\x05\xc8L\xe5\x1e%.\x98\x8a5\xc4\xc5\xd9\xf7\xdd\xd0\xdf\x0b\xc2\x8eg\xf93.wZ\xb5\xc1\x94B\xf8\xa2#\x82\x98a\xf9\xffY\x12\xe3v\x18L\xff\x14Fl\xeb\x0ff\x10\xc4\xb0\xa2\xb9y\xcd-\xba%\xcd\xa5\x8ajT\xd1\x92\xa9\x0c\x86x\xb6a\xe6h\xf8\x02<g\xaa\xaf\xf6\xdd%\x89\xae\x13z\xfe \xc6\x0b\xfb1^4p\x99\x1e6\xc6\xd4\xebK\xdbx\xf9\xc4\x8f[Iw\xf8\x89\xef\xcbQf\xcfh\xe3\x95\x8c\xebj&\xb9\xe2.\x8f\x0c\\ui\x89\xf1x\xf4\xd6\xc0kf\xc1\xf1v\xad(\xc4\xeb\x89~\xfa\xf0\x06\xa8\xa4\x7f\x93\xf4\xd7\x0c\xbcE#\xad\x92\xfc\xed\xeao\xefX\\\x03'
      >>> loads(old_pickle)
      [    0     a]
      [a + 1     1]