通过GF(2)

时间:2019-07-02 16:07:24

标签: python matrix cython linear-algebra finite-field

对于我当前的项目,我需要能够使用GF(2)中的条目来计算64 * 64矩阵的等级。我想知道是否有人有一个好的解决方案。

为此,我一直在使用pyfinite,但由于它是纯python实现,所以速度相当慢。我还尝试将我一直在使用的代码进行cythonise,但是由于依赖pyfinite而出现了问题。

我的下一个想法是用cython编写我自己的课程,但这似乎对我所需要的有些过大。

我需要以下功能

matrix = GF2Matrix(size=64) # creating a 64*64 matrix
matrix.setRow(i, [1,0,1....,1]) # set row using list
matrix += matrix2 # addition of matrices
rank(matrix) # then computing the rank

感谢任何想法。

1 个答案:

答案 0 :(得分:1)

有效表示GF(2)上的矩阵的一种方法是将行存储为整数,将每个整数解释为位字符串。例如4×4矩阵

[0 1 1 0]
[1 0 1 1]
[0 0 1 0]
[1 0 0 1]

(具有3级)可以表示为整数列表[6, 13, 4, 9]。在这里,我认为第一列与整数的最低有效位相对应,而最后一列与最高有效位相对应,但是相反的约定也适用。

使用这种表示形式,可以使用Python的按位整数运算有效地执行行运算:^用于加法,&用于乘法。 然后,您可以使用标准的高斯消去方法来计算等级。

这是一些相当有效的代码。给定如上所述表示矩阵的非负整数的集合rows,我们重复删除列表中的最后一行,然后使用该行从该列中消除所有1条目,这些列对应于其最低有效位。如果该行为零,则它没有最低有效位,也不会影响排名,因此我们只需要丢弃它并继续前进即可。

def gf2_rank(rows):
    """
    Find rank of a matrix over GF2.

    The rows of the matrix are given as nonnegative integers, thought
    of as bit-strings.

    This function modifies the input list. Use gf2_rank(rows.copy())
    instead of gf2_rank(rows) to avoid modifying rows.
    """
    rank = 0
    while rows:
        pivot_row = rows.pop()
        if pivot_row:
            rank += 1
            lsb = pivot_row & -pivot_row
            for index, row in enumerate(rows):
                if row & lsb:
                    rows[index] = row ^ pivot_row
    return rank

让我们对GF2上的随机64 x 64矩阵进行一些计时。 random_matrices是用于创建随机的64 x 64矩阵集合的函数:

import random

def random_matrix():
    return [random.getrandbits(64) for row in range(64)]

def random_matrices(count):
    return [random_matrix() for _ in range(count)]

这是计时代码:

import timeit

count = 1000
number = 10
timer = timeit.Timer(
    setup="ms = random_matrices({})".format(count),
    stmt="[gf2_rank(m.copy()) for m in ms]",
    globals=globals())
print(min(timer.repeat(number=number)) / count / number)

在我的机器(2.7 GHz Intel Core i7,macOS 10.14.5,Python 3.7)上打印的结果是0.0001984686384,因此对于单级计算而言,这是不到200µs的时间。

200µs对于纯Python等级计算而言是相当可观的,但是如果这还不够快,我们可以按照您的建议使用Cython。这是一个Cython函数,该函数采用dtype np.uint64的1d NumPy数组,再次将数组的每个元素都视为GF2上64×64矩阵的一行,并返回该矩阵的秩。 >

# cython: language_level=3, boundscheck=False

from libc.stdint cimport uint64_t, int64_t

def gf2_rank(uint64_t[:] rows):
    """
    Find rank of a matrix over GF2.

    The matrix can have no more than 64 columns, and is represented
    as a 1d NumPy array of dtype `np.uint64`. As before, each integer
    in the array is thought of as a bit-string to give a row of the
    matrix over GF2.

    This function modifies the input array.
    """
    cdef size_t i, j, nrows, rank
    cdef uint64_t pivot_row, row, lsb

    nrows = rows.shape[0]

    rank = 0
    for i in range(nrows):
        pivot_row = rows[i]
        if pivot_row:
            rank += 1
            lsb = pivot_row & -pivot_row
            for j in range(i + 1, nrows):
                row = rows[j]
                if row & lsb:
                    rows[j] = row ^ pivot_row

    return rank

运行64 x 64矩阵的等效时序,现在表示为dtype np.uint64和形状(64,)的NumPy数组,我得到的平均秩计算时间为7.56µs,快了25倍比纯Python版本要好。