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)