Constructing finite field arrays

GF(2) field class

The \(\mathrm{GF}(2)\) field is already constructed in galois. It can be accessed by galois.GF2.

In [1]: GF2 = galois.GF2

In [2]: print(GF2)
<Galois Field: GF(2^1), prim_poly = x + 1 (3 decimal)>

# The primitive element of the finite field
In [3]: GF2.alpha
Out[3]: GF(1, order=2)

# The primitive polynomial of the finite field
In [4]: GF2.prim_poly
Out[4]: Poly(x + 1, GF(2))

# The primitive element is a root of the primitive polynomial
In [5]: GF2.prim_poly(GF2.alpha)
Out[5]: GF(0, order=2)

GF(p) field classes

\(\mathrm{GF}(p)\) fields, where \(p\) is prime, can be constructed using the class factory galois.GF_factory.

In [6]: GF7 = galois.GF_factory(7, 1)

In [7]: print(GF7)
<Galois Field: GF(7^1), prim_poly = x + 4 (11 decimal)>

# The primitive element of the finite field
In [8]: GF7.alpha
Out[8]: GF(3, order=7)

# The primitive polynomial of the finite field
In [9]: GF7.prim_poly
Out[9]: Poly(x + 4, GF(7))

# The primitive element is a root of the primitive polynomial
In [10]: GF7.prim_poly(GF7.alpha)
Out[10]: GF(0, order=7)

GF(2^m) field classes

\(\mathrm{GF}(2^m)\) fields, where \(m\) is a positive integer, can be constructed using the class factory galois.GF_factory.

In [11]: GF8 = galois.GF_factory(2, 3)

In [12]: print(GF8)
<Galois Field: GF(2^3), prim_poly = x^3 + x + 1 (11 decimal)>

# The primitive element of the finite field
In [13]: GF8.alpha
Out[13]: GF(2, order=2^3)

# The primitive polynomial of the finite field
In [14]: GF8.prim_poly
Out[14]: Poly(x^3 + x + 1, GF(2))

# Convert the polynomial from GF(2)[x] to GF(8)[x]
In [15]: prim_poly = galois.Poly(GF8.prim_poly.coeffs, field=GF8); prim_poly
Out[15]: Poly(x^3 + x + 1, GF(2^3))

# The primitive element is a root of the primitive polynomial in GF(8)
In [16]: prim_poly(GF8.alpha)
Out[16]: GF(0, order=2^3)

Array creation: explicit and view casting

Galois field arrays can be constructed either explicitly or through numpy view casting. The method of array creation is the same for all Galois fields, but \(\mathrm{GF}(7)\) is used as an example here.

# Represents an existing numpy array
In [17]: x_np = np.random.randint(0, 7, 10, dtype=int); x_np
Out[17]: array([6, 4, 0, 5, 5, 6, 2, 4, 6, 1])

# Create a Galois field array through explicit construction (x_np is copied)
In [18]: x = GF7(x_np); x
Out[18]: GF([6, 4, 0, 5, 5, 6, 2, 4, 6, 1], order=7)

# View cast an existing array to a Galois field array (no copy operation)
In [19]: y = x_np.view(GF7); y
Out[19]: GF([6, 4, 0, 5, 5, 6, 2, 4, 6, 1], order=7)

Warning

View casting creates a pointer to the original data and simply interprets it as a new numpy.ndarray subclass, namely the Galois field classes. So, if the original array is modified so will the Galois field array.

In [20]: x_np
Out[20]: array([6, 4, 0, 5, 5, 6, 2, 4, 6, 1])

# Add 1 (mod 7) to the first element of x_np
In [21]: x_np[0] = (x_np[0] + 1) % 7; x_np
Out[21]: array([0, 4, 0, 5, 5, 6, 2, 4, 6, 1])

# Notice x is unchanged due to the copy during the explicit construction
In [22]: x
Out[22]: GF([6, 4, 0, 5, 5, 6, 2, 4, 6, 1], order=7)

# Notice y is changed due to view casting
In [23]: y
Out[23]: GF([0, 4, 0, 5, 5, 6, 2, 4, 6, 1], order=7)

Galois field array dtypes

Galois field arrays support all signed and unsigned integer dtypes, presuming the data type can store values in \([0, p^m)\).

In [24]: GF = galois.GF_factory(7, 1)

In [25]: a = GF.Random(10); a
Out[25]: GF([4, 4, 2, 0, 4, 0, 0, 3, 5, 4], order=7)

# Type cast an existing Galois field array to a different dtype
In [26]: a.astype(np.uint8)
Out[26]: GF([4, 4, 2, 0, 4, 0, 0, 3, 5, 4], order=7)

# Explicitly create a Galois field array with a specific dtype
In [27]: b = GF.Random(10, dtype=np.uint8); b
Out[27]: GF([6, 1, 1, 3, 6, 4, 2, 6, 0, 4], order=7)

Field element display modes

The default representation of a finite field element is the integer representation. That is, for \(\mathrm{GF}(2^3)\) the 8 field elements are represented as \(\{0, 1, 2, 3, 4, 5, 6, 7\}\). Alternatively, the field elements can be represented as degree-3 polynomials in \(\mathrm{GF}(2)[x]\), i.e. \(\{0, 1, x, x+1, x^2, x^2+1, x^2+x, x^2+x+1\}\).

In [28]: GF = galois.GF_factory(2, 3)

In [29]: a = GF.Random(10)

# The default mode represents the field elements as integers
In [30]: a
Out[30]: GF([0, 2, 6, 3, 6, 1, 2, 2, 3, 1], order=2^3)

# The display mode can be set to "poly" mode
In [31]: GF.display("poly"); a
Out[31]: GF([0, x, x^2 + x, x + 1, x^2 + x, 1, x, x, x + 1, 1], order=2^3)

# The polynomial variable can also be set
In [32]: GF.display("poly", "r"); a
Out[32]: GF([0, r, r^2 + r, r + 1, r^2 + r, 1, r, r, r + 1, 1], order=2^3)

# Reset the display mode to the default
In [33]: GF.display(); a
Out[33]: GF([0, 2, 6, 3, 6, 1, 2, 2, 3, 1], order=2^3)

The galois.GF.display method can be called as a context manager.

# The original display mode
In [34]: print(a)
GF([0, 2, 6, 3, 6, 1, 2, 2, 3, 1], order=2^3)

# The new display context
In [35]: with GF.display("poly"):
   ....:    print(a)
   ....: 
GF([0, x, x^2 + x, x + 1, x^2 + x, 1, x, x, x + 1, 1], order=2^3)

# Returns to the original display mode
In [36]: print(a)
GF([0, 2, 6, 3, 6, 1, 2, 2, 3, 1], order=2^3)