Dense matrices over F2e for 2e16 using the M4RIE library

The M4RIE library offers two matrix representations:

  1. mzed_t

m x n matrices over F2e are internally represented roughly as m x (en) matrices over F2. 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 F23 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(n3) additions but only O(n22e) multiplications.

  1. mzd_slice_t

m x n matrices over F2e are internally represented as slices of m x n matrices over F2. 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: 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
>>> 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:

Todo

Wrap mzd_slice_t.

REFERENCES:

class sage.matrix.matrix_gf2e_dense.M4RIE_finite_field[source]

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[source]

Bases: Matrix_dense

Create new matrix over GF(2e) for 2e16.

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: 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]
>>> 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)[source]

Augments self with right.

INPUT:

  • right – a matrix

EXAMPLES:

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
>>> 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)[source]

Pack the matrices over F2 into this matrix over F2e.

Elements in F2e can be represented as ciai where a is a root the minimal polynomial. If this matrix is A then this function writes ciai to the entry A[x,y] where ci is the entry Ci[x,y].

INPUT:

  • C – list of matrices over GF(2)

EXAMPLES:

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
>>> 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: B0, B1 = A.slice()
sage: B0 == A0 and B1 == A1
True
>>> from sage.all import *
>>> B0, B1 = A.slice()
>>> B0 == A0 and B1 == A1
True
echelonize(algorithm='heuristic', reduced=True, **kwds)[source]

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: 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
>>> 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
randomize(density=1, nonzero=False, *args, **kwds)[source]

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: 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)
>>> 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: 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)
>>> 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()[source]

Return the rank of this matrix (cached).

EXAMPLES:

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
>>> 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()[source]

Unpack this matrix into matrices over F2.

Elements in F2e can be represented as ciai where a is a root the minimal polynomial. This function returns a tuple of matrices C whose entry Ci[x,y] is the coefficient of ci in A[x,y] if this matrix is A.

EXAMPLES:

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
>>> 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: B = matrix(K, 5, 5)
sage: B.cling(A0, A1, A2)
sage: B == A
True
>>> 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)[source]

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: 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
>>> 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: 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
>>> 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)[source]

EXAMPLES:

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
>>> 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: 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]
>>> 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]