diff --git a/README.md b/README.md index 889654002..b4ee6a164 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ The new ufuncs are written in pure Python and [just-in-time compiled](https://nu ## Features -- Supports all [Galois fields](https://mhostetter.github.io/galois/latest/api/galois.GF/) $\mathrm{GF}(p^m)$, even arbitrarily-large fields! +- Supports all [Galois fields](https://mhostetter.github.io/galois/latest/api/galois.GF/) $\mathrm{GF}(p^m)$, even arbitrarily large fields! - [**Faster**](https://mhostetter.github.io/galois/latest/performance/prime-fields/) than native NumPy! `GF(x) * GF(y)` is faster than `(x * y) % p` for $\mathrm{GF}(p)$. - Seamless integration with NumPy -- normal NumPy functions work on [`FieldArray`](https://mhostetter.github.io/galois/latest/api/galois.FieldArray/)s. - Linear algebra over finite fields using normal [`np.linalg`](https://mhostetter.github.io/galois/latest/basic-usage/array-arithmetic/#linear-algebra) functions. @@ -76,7 +76,7 @@ Import the `galois` package in Python. In [1]: import galois In [2]: galois.__version__ -Out[2]: '0.4.0' +Out[2]: '0.4.1' ``` ### Create a [`FieldArray`](https://mhostetter.github.io/galois/latest/api/galois.FieldArray/) subclass diff --git a/docs/basic-usage/array-arithmetic.rst b/docs/basic-usage/array-arithmetic.rst index c71fe4b4e..dc7c7031d 100644 --- a/docs/basic-usage/array-arithmetic.rst +++ b/docs/basic-usage/array-arithmetic.rst @@ -292,7 +292,7 @@ Advanced arithmetic :collapsible: The Discrete Fourier Transform (DFT) of size $n$ over the finite field $\mathrm{GF}(p^m)$ exists when - there exists a primitive $n$-th root of unity. This occurs when $n\ |\ p^m - 1$. + there exists a primitive $n$-th root of unity. This occurs when $n \mid p^m - 1$. .. ipython-with-reprs:: int,poly,power @@ -310,7 +310,7 @@ Advanced arithmetic :collapsible: The inverse Discrete Fourier Transform (DFT) of size $n$ over the finite field $\mathrm{GF}(p^m)$ - exists when there exists a primitive $n$-th root of unity. This occurs when $n\ |\ p^m - 1$. + exists when there exists a primitive $n$-th root of unity. This occurs when $n \mid p^m - 1$. .. ipython-with-reprs:: int,poly,power diff --git a/docs/conf.py b/docs/conf.py index fb95cec6d..bcc6f39b5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -327,6 +327,14 @@ "icon": "fontawesome/solid/quote-left", "override": True, }, + { + "name": "question", + "title": "Question", + "classes": ["collapsible"], + "icon": "fontawesome/solid/question", + "color": (108, 117, 125), # --sd-color-secondary + "override": True, + }, { "name": "seealso", "title": "See also", diff --git a/docs/index.rst b/docs/index.rst index 1e7af191a..531607d2a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -40,7 +40,7 @@ calculation (for memory savings). Features -------- -- Supports all Galois fields $\mathrm{GF}(p^m)$, even arbitrarily-large fields! +- Supports all Galois fields $\mathrm{GF}(p^m)$, even arbitrarily large fields! - **Faster** than native NumPy! `GF(x) * GF(y)` is faster than `(x * y) % p` for $\mathrm{GF}(p)$. - Seamless integration with NumPy -- normal NumPy functions work on :obj:`~galois.FieldArray` instances. - Linear algebra over finite fields using normal :obj:`numpy.linalg` functions. diff --git a/docs/release-notes/v0.0.md b/docs/release-notes/v0.0.md index 8da4e5d0f..129427b46 100644 --- a/docs/release-notes/v0.0.md +++ b/docs/release-notes/v0.0.md @@ -307,7 +307,7 @@ Poly(1, GF(2)) - Allow polynomial comparison with integers and field scalars. Now `galois.Poly([0]) == 0` and `galois.Poly([0]) == GF(0)` return `True` rather than raising `TypeError`. - Support testing 0-degree polynomials for irreducibility and primitivity. - Extend `crt()` to work over non co-prime moduli. -- Extend `prev_prime()` and `next_prime()` to work over arbitrarily-large inputs. +- Extend `prev_prime()` and `next_prime()` to work over arbitrarily large inputs. - Allow negative integer inputs to `primes()`, `is_prime()`, `is_composite()`, `is_prime_power()`, `is_perfect_power()`, `is_square_free()`, `is_smooth()`, and `is_powersmooth()`. - Fix various type hinting errors. - Various other bug fixes. @@ -715,8 +715,8 @@ Poly(1, GF(2)) - Moved `galois.square_free_factorization()` function into `Poly.square_free_factors()` method. ([#362](https://github.com/mhostetter/galois/pull/362)) - Moved `galois.distinct_degree_factorization()` function into `Poly.distinct_degree_factors()` method. ([#362](https://github.com/mhostetter/galois/pull/362)) - Moved `galois.equal_degree_factorization()` function into `Poly.equal_degree_factors()` method. ([#362](https://github.com/mhostetter/galois/pull/362)) -- Moved `galois.is_irreducible()` function into `Poly.is_irreducible()` method. This is a method, not property, to indicate it is a computationally-expensive operation. ([#362](https://github.com/mhostetter/galois/pull/362)) -- Moved `galois.is_primitive()` function into `Poly.is_primitive()` method. This is a method, not property, to indicate it is a computationally-expensive operation. ([#362](https://github.com/mhostetter/galois/pull/362)) +- Moved `galois.is_irreducible()` function into `Poly.is_irreducible()` method. This is a method, not property, to indicate it is a computationally expensive operation. ([#362](https://github.com/mhostetter/galois/pull/362)) +- Moved `galois.is_primitive()` function into `Poly.is_primitive()` method. This is a method, not property, to indicate it is a computationally expensive operation. ([#362](https://github.com/mhostetter/galois/pull/362)) - Moved `galois.is_monic()` function into `Poly.is_monic` property. ([#362](https://github.com/mhostetter/galois/pull/362)) ### Changes @@ -742,7 +742,7 @@ Poly(1, GF(2)) - Added `galois.get_printoptions()` function to return the current package-wide printing options. This is the equivalent of `np.get_printoptions()`. ([#363](https://github.com/mhostetter/galois/pull/363)) - Added `galois.printoptions()` context manager to modify printing options inside of a `with` statement. This is the equivalent of `np.printoptions()`. ([#363](https://github.com/mhostetter/galois/pull/363)) - Added a separate `Poly.factors()` method, in addition to the polymorphic `galois.factors()`. ([#362](https://github.com/mhostetter/galois/pull/362)) -- Added a separate `Poly.is_square_free()` method, in addition to the polymorphic `galois.is_square_free()`. This is a method, not property, to indicate it is a computationally-expensive operation. ([#362](https://github.com/mhostetter/galois/pull/362)) +- Added a separate `Poly.is_square_free()` method, in addition to the polymorphic `galois.is_square_free()`. This is a method, not property, to indicate it is a computationally expensive operation. ([#362](https://github.com/mhostetter/galois/pull/362)) - Fixed a bug (believed to be introduced in v0.0.26) where `Poly.degree` occasionally returned `np.int64` instead of `int`. This could cause overflow in certain large integer operations (e.g., computing $q^m$ when determining if a degree-$m$ polynomial over $\mathrm{GF}(q)$ is irreducible). When the integer overflowed, this created erroneous results. ([#360](https://github.com/mhostetter/galois/issues/360), [#361](https://github.com/mhostetter/galois/pull/361)) - Increased code coverage. @@ -756,7 +756,7 @@ Poly(1, GF(2)) ### Changes -- Added support for NumPy 1.22 with Numba 0.55.2. This allows users to upgrade NumPy and avoid recently-discovered vulnerabilities [CVE-2021-34141](https://nvd.nist.gov/vuln/detail/CVE-2021-34141), [CVE-2021-41496](https://nvd.nist.gov/vuln/detail/CVE-2021-41496), and [CVE-2021-41495](https://nvd.nist.gov/vuln/detail/CVE-2021-41495). ([#366](https://github.com/mhostetter/galois/pull/366)) +- Added support for NumPy 1.22 with Numba 0.55.2. This allows users to upgrade NumPy and avoid recently discovered vulnerabilities [CVE-2021-34141](https://nvd.nist.gov/vuln/detail/CVE-2021-34141), [CVE-2021-41496](https://nvd.nist.gov/vuln/detail/CVE-2021-41496), and [CVE-2021-41495](https://nvd.nist.gov/vuln/detail/CVE-2021-41495). ([#366](https://github.com/mhostetter/galois/pull/366)) - Made `FieldArray.repr_table()` more compact. ([#367](https://github.com/mhostetter/galois/pull/367)) ```ipython In [2]: GF = galois.GF(3**3) diff --git a/docs/release-notes/v0.3.md b/docs/release-notes/v0.3.md index 2d6235245..ed0784991 100644 --- a/docs/release-notes/v0.3.md +++ b/docs/release-notes/v0.3.md @@ -173,7 +173,7 @@ tocdepth: 2 Poly(x^9 + 3x^2 + 4, GF(7)) ``` - Added a database of binary irreducible polynomials with degrees less than 10,000. These polynomials are - lexicographically-first and have the minimum number of non-zero terms. The database is accessed in + lexicographically first and have the minimum number of non-zero terms. The database is accessed in `irreducible_poly()` when `terms="min"` and `method="min"`. ([#462](https://github.com/mhostetter/galois/pull/462)) ```ipython In [1]: import galois diff --git a/docs/release-notes/v0.4.md b/docs/release-notes/v0.4.md index 7e1c3ac9a..73edda7c9 100644 --- a/docs/release-notes/v0.4.md +++ b/docs/release-notes/v0.4.md @@ -17,3 +17,20 @@ tocdepth: 2 - Iyán Méndez Veiga ([@iyanmv](https://github.com/iyanmv)) - Matt Hostetter ([@mhostetter](https://github.com/mhostetter)) + +## v0.4.1 + +*Released July 6, 2024* + +### Changes + +- Fixed multithreading incompatibility. Previously only one thread could read from the SQLite databases. ([#558](https://github.com/mhostetter/galois/pull/558)) +- Clarified `Poly` string representation when underlying field is non-primitive and uses the `"poly"` element representation. ([#329](https://github.com/mhostetter/galois/issues/329)) +- Fixed typo in error message. ([#557](https://github.com/mhostetter/galois/pull/557)) +- Made minor documentation improvements. + +### Contributors + +- Semjon Kravtšenko ([@semjon00](https://github.com/semjon00)) +- [@MrVeka](https://github.com/MrVeka) +- Matt Hostetter ([@mhostetter](https://github.com/mhostetter)) diff --git a/src/galois/_codes/_linear.py b/src/galois/_codes/_linear.py index f5d9889b7..9fd650df5 100644 --- a/src/galois/_codes/_linear.py +++ b/src/galois/_codes/_linear.py @@ -383,11 +383,11 @@ def generator_to_parity_check_matrix(G: FieldArray) -> FieldArray: Arguments: G: The $(k, n)$ generator matrix $\mathbf{G}$ in systematic form - $\mathbf{G} = [\mathbf{I}_{k,k}\ |\ \mathbf{P}_{k,n-k}]$. + $\mathbf{G} = [\mathbf{I}_{k,k} \mid \mathbf{P}_{k,n-k}]$. Returns: The $(n-k, n)$ parity-check matrix - $\mathbf{H} = [-\mathbf{P}_{k,n-k}^T\ |\ \mathbf{I}_{n-k,n-k}]$`. + $\mathbf{H} = [-\mathbf{P}_{k,n-k}^T \mid \mathbf{I}_{n-k,n-k}]$`. Examples: .. ipython:: python @@ -423,10 +423,10 @@ def parity_check_to_generator_matrix(H: FieldArray) -> FieldArray: Arguments: H: The $(n-k, n)$ parity-check matrix $\mathbf{G}$ in systematic form - $\mathbf{H} = [-\mathbf{P}_{k,n-k}^T\ |\ \mathbf{I}_{n-k,n-k}]$`. + $\mathbf{H} = [-\mathbf{P}_{k,n-k}^T \mid \mathbf{I}_{n-k,n-k}]$`. Returns: - The $(k, n)$ generator matrix $\mathbf{G} = [\mathbf{I}_{k,k}\ |\ \mathbf{P}_{k,n-k}]$. + The $(k, n)$ generator matrix $\mathbf{G} = [\mathbf{I}_{k,k} \mid \mathbf{P}_{k,n-k}]$. Examples: .. ipython:: python diff --git a/src/galois/_databases/_interface.py b/src/galois/_databases/_interface.py index afaf270bd..443a6bfbd 100644 --- a/src/galois/_databases/_interface.py +++ b/src/galois/_databases/_interface.py @@ -7,6 +7,7 @@ import sqlite3 import sys from pathlib import Path +from threading import Lock class DatabaseInterface: @@ -14,15 +15,17 @@ class DatabaseInterface: An abstract class to interface with SQLite databases. """ - singleton = None + _lock = Lock() + _singleton = None file: Path def __new__(cls): - if cls.singleton is None: - cls.singleton = super().__new__(cls) - cls.conn = sqlite3.connect(cls.file) - cls.cursor = cls.conn.cursor() - return cls.singleton + with cls._lock: + if cls._singleton is None: + cls._singleton = super().__new__(cls) + cls.conn = sqlite3.connect(cls.file, check_same_thread=False) + cls.cursor = cls.conn.cursor() + return cls._singleton class PrimeFactorsDatabase(DatabaseInterface): @@ -30,7 +33,6 @@ class PrimeFactorsDatabase(DatabaseInterface): A class to interface with the prime factors database. """ - singleton = None file = Path(__file__).parent / "prime_factors.db" def fetch(self, n: int) -> tuple[list[int], list[int], int]: @@ -51,15 +53,16 @@ def fetch(self, n: int) -> tuple[list[int], list[int], int]: n = str(n) sys.set_int_max_str_digits(default_limit) - self.cursor.execute( - """ - SELECT factors, multiplicities, composite - FROM factorizations - WHERE value=? - """, - (str(n),), - ) - result = self.cursor.fetchone() + with self._lock: + self.cursor.execute( + """ + SELECT factors, multiplicities, composite + FROM factorizations + WHERE value=? + """, + (str(n),), + ) + result = self.cursor.fetchone() if result is None: raise LookupError(f"The prime factors database does not contain an entry for {n}.") @@ -76,7 +79,6 @@ class IrreduciblePolyDatabase(DatabaseInterface): A class to interface with the irreducible polynomials database. """ - singleton = None file = Path(__file__).parent / "irreducible_polys.db" def fetch(self, characteristic: int, degree: int) -> tuple[list[int], list[int]]: @@ -90,14 +92,15 @@ def fetch(self, characteristic: int, degree: int) -> tuple[list[int], list[int]] Returns: A tuple containing the non-zero degrees and coefficients of the irreducible polynomial. """ - self.cursor.execute( - """ - SELECT nonzero_degrees, nonzero_coeffs - FROM polys - WHERE characteristic=? AND degree=?""", - (characteristic, degree), - ) - result = self.cursor.fetchone() + with self._lock: + self.cursor.execute( + """ + SELECT nonzero_degrees, nonzero_coeffs + FROM polys + WHERE characteristic=? AND degree=?""", + (characteristic, degree), + ) + result = self.cursor.fetchone() if result is None: raise LookupError( @@ -116,7 +119,6 @@ class ConwayPolyDatabase(DatabaseInterface): A class to interface with the Conway polynomials database. """ - singleton = None file = Path(__file__).parent / "conway_polys.db" def fetch(self, characteristic: int, degree: int) -> tuple[list[int], list[int]]: @@ -130,15 +132,16 @@ def fetch(self, characteristic: int, degree: int) -> tuple[list[int], list[int]] Returns: A tuple containing the non-zero degrees and coefficients of the Conway polynomial. """ - self.cursor.execute( - """ - SELECT nonzero_degrees, nonzero_coeffs - FROM polys - WHERE characteristic=? AND degree=? - """, - (characteristic, degree), - ) - result = self.cursor.fetchone() + with self._lock: + self.cursor.execute( + """ + SELECT nonzero_degrees, nonzero_coeffs + FROM polys + WHERE characteristic=? AND degree=? + """, + (characteristic, degree), + ) + result = self.cursor.fetchone() if result is None: raise LookupError( diff --git a/src/galois/_fields/_array.py b/src/galois/_fields/_array.py index 2895a4563..3a074bc92 100644 --- a/src/galois/_fields/_array.py +++ b/src/galois/_fields/_array.py @@ -128,7 +128,7 @@ def _verify_array_like_types_and_values(cls, x: ElementLike | ArrayLike) -> Elem if isinstance(x, (int, np.integer)): cls._verify_scalar_value(x) elif isinstance(x, cls): - # This was a previously-created and vetted array -- there's no need to re-verify + # This was a previously created and vetted array -- there's no need to re-verify if x.ndim == 0: # Ensure that in "large" fields with dtype=object that FieldArray objects aren't assigned to the array. # The arithmetic functions are designed to operate on Python ints. diff --git a/src/galois/_fields/_factory.py b/src/galois/_fields/_factory.py index caf19b1b9..7bce014ab 100644 --- a/src/galois/_fields/_factory.py +++ b/src/galois/_fields/_factory.py @@ -88,7 +88,7 @@ def GF( :func:`~galois.FieldArray.compile` method. See :doc:`/basic-usage/compilation-modes` for a further discussion. - - `None` (default): For a newly-created :obj:`~galois.FieldArray` subclass, `None` corresponds to + - `None` (default): For a newly created :obj:`~galois.FieldArray` subclass, `None` corresponds to `"auto"`. If the :obj:`~galois.FieldArray` subclass already exists, `None` does not modify its current compilation mode. - `"auto"`: Selects `"jit-lookup"` for fields with order less than $2^{20}$, `"jit-calculate"` for @@ -109,7 +109,7 @@ def GF( :func:`~galois.FieldArray.repr` method. See :doc:`/basic-usage/element-representation` for a further discussion. - - `None` (default): For a newly-created :obj:`~galois.FieldArray` subclass, `None` corresponds to `"int"`. + - `None` (default): For a newly created :obj:`~galois.FieldArray` subclass, `None` corresponds to `"int"`. If the :obj:`~galois.FieldArray` subclass already exists, `None` does not modify its current element representation. - `"int"`: Sets the element representation to the :ref:`integer representation `. @@ -191,7 +191,7 @@ def GF( GF = galois.GF(3**5, irreducible_poly="x^5 + 2x + 2") print(GF.properties) - Finite fields with arbitrarily-large orders are supported. + Finite fields with arbitrarily large orders are supported. .. md-tab-set:: @@ -246,7 +246,7 @@ def GF( verify_isinstance(order, int) p, e = factors(order) if not len(p) == len(e) == 1: - s = " + ".join([f"{pi}**{ei}" for pi, ei in zip(p, e)]) + s = " * ".join([f"{pi}^{ei}" for pi, ei in zip(p, e)]) raise ValueError(f"Argument 'order' must be a prime power, not {order} = {s}.") characteristic, degree = p[0], e[0] elif len(args) == 2: diff --git a/src/galois/_modular.py b/src/galois/_modular.py index 26e4ae021..8ef33ff12 100644 --- a/src/galois/_modular.py +++ b/src/galois/_modular.py @@ -82,7 +82,7 @@ def euler_phi(n: int) -> int: Notes: This function implements the Euler totient function - $$\phi(n) = n \prod_{p\ |\ n} \bigg(1 - \frac{1}{p}\bigg) = \prod_{i=1}^{k} p_i^{e_i-1} \big(p_i - 1\big)$$ + $$\phi(n) = n \prod_{p \mid n} \bigg(1 - \frac{1}{p}\bigg) = \prod_{i=1}^{k} p_i^{e_i-1} \big(p_i - 1\big)$$ for prime $p$ and the prime factorization $n = p_1^{e_1} \dots p_k^{e_k}$. diff --git a/src/galois/_polys/_conversions.py b/src/galois/_polys/_conversions.py index 18aeafac1..3af41b6ab 100644 --- a/src/galois/_polys/_conversions.py +++ b/src/galois/_polys/_conversions.py @@ -94,10 +94,21 @@ def sparse_poly_to_str(degrees: list[int], coeffs: list[int], poly_var: str = "x # Use brackets around coefficients only when using the "poly" or "power" element representation brackets = hasattr(type(coeffs), "_element_repr") and type(coeffs)._element_repr in ["poly", "power"] + # If the element representation is polynomial and the irreducible polynomial is not primitive, convert x to z + # for the coefficients + convert_x_to_z = ( + hasattr(type(coeffs), "_element_repr") + and type(coeffs)._element_repr == "poly" + and not type(coeffs).is_primitive_poly + ) + for degree, coeff in zip(degrees, coeffs): if coeff == 0: continue + if convert_x_to_z and coeff != 1: + coeff = str(coeff).replace("x", "z") + if degree > 1: if coeff == 1: c = "" diff --git a/src/galois/_polys/_conway.py b/src/galois/_polys/_conway.py index 59c81c99c..23710c929 100644 --- a/src/galois/_polys/_conway.py +++ b/src/galois/_polys/_conway.py @@ -24,7 +24,7 @@ def is_conway(f: Poly, search: bool = False) -> bool: .. question:: Why is this a method and not a property? :collapsible: - This is a method to indicate it is a computationally-expensive task. + This is a method to indicate it is a computationally expensive task. Arguments: search: Manually search for Conway polynomials if they are not included in `Frank Luebeck's database @@ -47,10 +47,10 @@ def is_conway(f: Poly, search: bool = False) -> bool: Notes: A degree-$m$ polynomial $f(x)$ over $\mathrm{GF}(p)$ is the *Conway polynomial* $C_{p,m}(x)$ if it is monic, primitive, compatible with Conway polynomials $C_{p,n}(x)$ for all - $n\ |\ m$, and is lexicographically first according to a special ordering. + $n \mid m$, and is lexicographically first according to a special ordering. A Conway polynomial $C_{p,m}(x)$ is *compatible* with Conway polynomials $C_{p,n}(x)$ for - $n\ |\ m$ if $C_{p,n}(x^r)$ divides $C_{p,m}(x)$, where $r = \frac{p^m - 1}{p^n - 1}$. + $n \mid m$ if $C_{p,n}(x^r)$ divides $C_{p,m}(x)$, where $r = \frac{p^m - 1}{p^n - 1}$. The Conway lexicographic ordering is defined as follows. Given two degree-$m$ polynomials $g(x) = \sum_{i=0}^m g_i x^i$ and $h(x) = \sum_{i=0}^m h_i x^i$, then $g < h$ if and only if @@ -81,7 +81,7 @@ def is_conway(f: Poly, search: bool = False) -> bool: f.is_conway_consistent() g.is_conway_consistent() - Among the multiple candidate Conway polynomials, the lexicographically-first (accordingly to a special + Among the multiple candidate Conway polynomials, the lexicographically first (accordingly to a special lexicographical order) is the Conway polynomial. .. ipython:: python @@ -106,12 +106,12 @@ def is_conway(f: Poly, search: bool = False) -> bool: def is_conway_consistent(f: Poly, search: bool = False) -> bool: r""" Determines whether the degree-$m$ polynomial $f(x)$ over $\mathrm{GF}(p)$ is consistent - with smaller Conway polynomials $C_{p,n}(x)$ for all $n\ |\ m$. + with smaller Conway polynomials $C_{p,n}(x)$ for all $n \mid m$. .. question:: Why is this a method and not a property? :collapsible: - This is a method to indicate it is a computationally-expensive task. + This is a method to indicate it is a computationally expensive task. Arguments: search: Manually search for Conway polynomials if they are not included in `Frank Luebeck's database @@ -123,7 +123,7 @@ def is_conway_consistent(f: Poly, search: bool = False) -> bool: Returns: `True` if the polynomial $f(x)$ is primitive and consistent with smaller Conway polynomials - $C_{p,n}(x)$ for all $n\ |\ m$. + $C_{p,n}(x)$ for all $n \mid m$. Raises: LookupError: If `search=False` and a smaller Conway polynomial $C_{p,n}$ is not found in Frank Luebeck's @@ -134,7 +134,7 @@ def is_conway_consistent(f: Poly, search: bool = False) -> bool: Notes: A degree-$m$ polynomial $f(x)$ over $\mathrm{GF}(p)$ is *compatible* with Conway polynomials - $C_{p,n}(x)$ for $n\ |\ m$ if $C_{p,n}(x^r)$ divides $f(x)$, where + $C_{p,n}(x)$ for $n \mid m$ if $C_{p,n}(x^r)$ divides $f(x)$, where $r = \frac{p^m - 1}{p^n - 1}$. A Conway-consistent polynomial has all the properties of a Conway polynomial except that it is not @@ -164,7 +164,7 @@ def is_conway_consistent(f: Poly, search: bool = False) -> bool: f.is_conway_consistent() g.is_conway_consistent() - Among the multiple candidate Conway polynomials, the lexicographically-first (accordingly to a special + Among the multiple candidate Conway polynomials, the lexicographically first (accordingly to a special lexicographical order) is the Conway polynomial. .. ipython:: python @@ -230,10 +230,10 @@ def conway_poly(characteristic: int, degree: int, search: bool = False) -> Poly: Notes: A degree-$m$ polynomial $f(x)$ over $\mathrm{GF}(p)$ is the *Conway polynomial* $C_{p,m}(x)$ if it is monic, primitive, compatible with Conway polynomials $C_{p,n}(x)$ for all - $n\ |\ m$, and is lexicographically first according to a special ordering. + $n \mid m$, and is lexicographically first according to a special ordering. A Conway polynomial $C_{p,m}(x)$ is *compatible* with Conway polynomials $C_{p,n}(x)$ for - $n\ |\ m$ if $C_{p,n}(x^r)$ divides $C_{p,m}(x)$, where $r = \frac{p^m - 1}{p^n - 1}$. + $n \mid m$ if $C_{p,n}(x^r)$ divides $C_{p,m}(x)$, where $r = \frac{p^m - 1}{p^n - 1}$. The Conway lexicographic ordering is defined as follows. Given two degree-$m$ polynomials $g(x) = \sum_{i=0}^m g_i x^i$ and $h(x) = \sum_{i=0}^m h_i x^i$, then $g < h$ if and only if @@ -268,7 +268,7 @@ def conway_poly(characteristic: int, degree: int, search: bool = False) -> Poly: f.is_conway_consistent() g.is_conway_consistent() - Among the multiple candidate Conway polynomials, the lexicographically-first (accordingly to a special + Among the multiple candidate Conway polynomials, the lexicographically first (accordingly to a special lexicographical order) is the Conway polynomial. .. ipython:: python diff --git a/src/galois/_polys/_dense.py b/src/galois/_polys/_dense.py index 6edd1e80e..c7a5fca35 100644 --- a/src/galois/_polys/_dense.py +++ b/src/galois/_polys/_dense.py @@ -335,7 +335,7 @@ def __call__(self, a: Array, b: int, c: Array | None = None) -> Array: assert a.ndim == 1 and c.ndim == 1 if c is not None else True dtype = a.dtype - # Convert the integer b into a vector of uint64 [MSWord, ..., LSWord] so arbitrarily-large exponents may be + # Convert the integer b into a vector of uint64 [MSWord, ..., LSWord] so arbitrarily large exponents may be # passed into the JIT-compiled version b_vec = [] # Pop on LSWord -> MSWord while b >= 2**64: @@ -366,7 +366,7 @@ def set_globals(self): @staticmethod def implementation(a, b_vec, c): """ - b is a vector of uint64 [MSWord, ..., LSWord] so that arbitrarily-large exponents may be passed + b is a vector of uint64 [MSWord, ..., LSWord] so that arbitrarily large exponents may be passed """ if b_vec.size == 1 and b_vec[0] == 0: return np.array([1], dtype=a.dtype) diff --git a/src/galois/_polys/_factor.py b/src/galois/_polys/_factor.py index 3922edfed..59a440630 100644 --- a/src/galois/_polys/_factor.py +++ b/src/galois/_polys/_factor.py @@ -19,7 +19,7 @@ def is_square_free(f) -> bool: .. question:: Why is this a method and not a property? :collapsible: - This is a method to indicate it is a computationally-expensive task. + This is a method to indicate it is a computationally expensive task. Returns: `True` if the polynomial is square-free. diff --git a/src/galois/_polys/_irreducible.py b/src/galois/_polys/_irreducible.py index 9fd0828b3..e0541ef43 100644 --- a/src/galois/_polys/_irreducible.py +++ b/src/galois/_polys/_irreducible.py @@ -33,7 +33,7 @@ def is_irreducible(f: Poly) -> bool: .. question:: Why is this a method and not a property? :collapsible: - This is a method to indicate it is a computationally-expensive task. + This is a method to indicate it is a computationally expensive task. Returns: `True` if the polynomial is irreducible. @@ -49,7 +49,7 @@ def is_irreducible(f: Poly) -> bool: This function implements Rabin's irreducibility test. It says a degree-$m$ polynomial $f(x)$ over $\mathrm{GF}(q)$ for prime power $q$ is irreducible if and only if - $f(x)\ |\ (x^{q^m} - x)$ and $\textrm{gcd}(f(x),\ x^{q^{m_i}} - x) = 1$ for + $f(x) \mid (x^{q^m} - x)$ and $\textrm{gcd}(f(x),\ x^{q^{m_i}} - x) = 1$ for $1 \le i \le k$, where $m_i = m/p_i$ for the $k$ prime divisors $p_i$ of $m$. References: @@ -145,8 +145,8 @@ def irreducible_poly( method: The search method for finding the irreducible polynomial. - - `"min"` (default): Returns the lexicographically-first polynomial. - - `"max"`: Returns the lexicographically-last polynomial. + - `"min"` (default): Returns the lexicographically first polynomial. + - `"max"`: Returns the lexicographically last polynomial. - `"random"`: Returns a random polynomial. .. fast-performance:: @@ -176,7 +176,7 @@ def irreducible_poly( $\mathrm{GF}(q)$. Examples: - Find the lexicographically-first, lexicographically-last, and a random monic irreducible polynomial. + Find the lexicographically first, lexicographically last, and a random monic irreducible polynomial. .. ipython:: python @@ -184,13 +184,13 @@ def irreducible_poly( galois.irreducible_poly(7, 3, method="max") galois.irreducible_poly(7, 3, method="random") - Find the lexicographically-first monic irreducible polynomial with four terms. + Find the lexicographically first monic irreducible polynomial with four terms. .. ipython:: python galois.irreducible_poly(7, 3, terms=4) - Find the lexicographically-first monic irreducible polynomial with the minimum number of non-zero terms. + Find the lexicographically first monic irreducible polynomial with the minimum number of non-zero terms. .. ipython:: python diff --git a/src/galois/_polys/_poly.py b/src/galois/_polys/_poly.py index 36c12a203..7ea7c72a7 100644 --- a/src/galois/_polys/_poly.py +++ b/src/galois/_polys/_poly.py @@ -903,7 +903,7 @@ def is_conway(self) -> bool: def is_conway_consistent(self) -> bool: r""" Determines whether the degree-$m$ polynomial $f(x)$ over $\mathrm{GF}(p)$ is consistent - with smaller Conway polynomials $C_{p,n}(x)$ for all $n\ |\ m$. + with smaller Conway polynomials $C_{p,n}(x)$ for all $n \mid m$. """ # Will be monkey-patched in `_conway.py` raise NotImplementedError diff --git a/src/galois/_polys/_primitive.py b/src/galois/_polys/_primitive.py index 49e8470f0..d97fa038f 100644 --- a/src/galois/_polys/_primitive.py +++ b/src/galois/_polys/_primitive.py @@ -32,7 +32,7 @@ def is_primitive(f: Poly) -> bool: .. question:: Why is this a method and not a property? :collapsible: - This is a method to indicate it is a computationally-expensive task. + This is a method to indicate it is a computationally expensive task. Returns: `True` if the polynomial is primitive. @@ -42,7 +42,7 @@ def is_primitive(f: Poly) -> bool: Notes: A degree-$m$ polynomial $f(x)$ over $\mathrm{GF}(q)$ is *primitive* if it is - irreducible and $f(x)\ |\ (x^k - 1)$ for $k = q^m - 1$ and no $k$ less than + irreducible and $f(x) \mid (x^k - 1)$ for $k = q^m - 1$ and no $k$ less than $q^m - 1$. References: @@ -125,8 +125,8 @@ def primitive_poly( method: The search method for finding the primitive polynomial. - - `"min"` (default): Returns the lexicographically-first polynomial. - - `"max"`: Returns the lexicographically-last polynomial. + - `"min"` (default): Returns the lexicographically first polynomial. + - `"max"`: Returns the lexicographically last polynomial. - `"random"`: Returns a random polynomial. Returns: @@ -149,7 +149,7 @@ def primitive_poly( $\mathrm{GF}(q^m) = \{0, 1, \alpha, \alpha^2, \dots, \alpha^{q^m-2}\}$. Examples: - Find the lexicographically-first, lexicographically-last, and a random monic primitive polynomial. + Find the lexicographically first, lexicographically last, and a random monic primitive polynomial. .. ipython:: python @@ -157,20 +157,20 @@ def primitive_poly( galois.primitive_poly(7, 3, method="max") galois.primitive_poly(7, 3, method="random") - Find the lexicographically-first monic primitive polynomial with four terms. + Find the lexicographically first monic primitive polynomial with four terms. .. ipython:: python galois.primitive_poly(7, 3, terms=4) - Find the lexicographically-first monic irreducible polynomial with the minimum number of non-zero terms. + Find the lexicographically first monic irreducible polynomial with the minimum number of non-zero terms. .. ipython:: python galois.primitive_poly(7, 3, terms="min") - Notice :func:`~galois.primitive_poly` returns the lexicographically-first primitive polynomial but - :func:`~galois.conway_poly` returns the lexicographically-first primitive polynomial that is *consistent* + Notice :func:`~galois.primitive_poly` returns the lexicographically first primitive polynomial but + :func:`~galois.conway_poly` returns the lexicographically first primitive polynomial that is *consistent* with smaller Conway polynomials. This is sometimes the same polynomial. .. ipython:: python @@ -371,7 +371,7 @@ def matlab_primitive_poly(characteristic: int, degree: int) -> Poly: Poly.is_primitive, primitive_poly, conway_poly Notes: - This function returns the same result as Matlab's `gfprimdf(m, p)`. Matlab uses the lexicographically-first + This function returns the same result as Matlab's `gfprimdf(m, p)`. Matlab uses the lexicographically first primitive polynomial with minimum terms, which is equivalent to `galois.primitive_poly(p, m, terms="min")`. There are three notable exceptions, however: @@ -416,18 +416,18 @@ def matlab_primitive_poly(characteristic: int, degree: int) -> Poly: f"Argument 'degree' must be at least 1, not {degree}. There are no primitive polynomials with degree 0." ) - # Textbooks and Matlab use the lexicographically-first primitive polynomial with minimal terms for the default. + # Textbooks and Matlab use the lexicographically first primitive polynomial with minimal terms for the default. # But for some reason, there are three exceptions. I can't determine why. if characteristic == 2 and degree == 7: - # Not the lexicographically-first of x^7 + x + 1. + # Not the lexicographically first of x^7 + x + 1. return Poly.Degrees([7, 3, 0]) if characteristic == 2 and degree == 14: - # Not the lexicographically-first of x^14 + x^5 + x^3 + x + 1. + # Not the lexicographically first of x^14 + x^5 + x^3 + x + 1. return Poly.Degrees([14, 10, 6, 1, 0]) if characteristic == 2 and degree == 16: - # Not the lexicographically-first of x^16 + x^5 + x^3 + x^2 + 1. + # Not the lexicographically first of x^16 + x^5 + x^3 + x^2 + 1. return Poly.Degrees([16, 12, 3, 1, 0]) return primitive_poly(characteristic, degree) diff --git a/src/galois/_prime.py b/src/galois/_prime.py index 0ab7f2d60..9b80ae73d 100644 --- a/src/galois/_prime.py +++ b/src/galois/_prime.py @@ -89,7 +89,7 @@ def primes(n: int) -> list[int]: p = (prime_idxs * 2 + 3).tolist() # Convert indices back to odd integers p.insert(0, 2) # Add the only even prime, 2 - # Replace the global primes lookup table with the newly-created, larger list + # Replace the global primes lookup table with the newly created, larger list PRIMES = p MAX_K = len(PRIMES) MAX_N = n @@ -97,7 +97,7 @@ def primes(n: int) -> list[int]: return p -# TODO: Don't build a large lookup table at import time. Instead, use the progressively-growing nature of PRIMES. +# TODO: Don't build a large lookup table at import time. Instead, use the progressively growing nature of PRIMES. # Generate a prime lookup table for efficient lookup in other algorithms PRIMES = primes(10_000_000) MAX_K = len(PRIMES) @@ -644,7 +644,7 @@ def legendre_symbol(a: int, p: int) -> int: .. math:: \bigg(\frac{a}{p}\bigg) = \begin{cases} - 0, & p\ |\ a + 0, & p \mid a 1, & a \in Q_p @@ -1278,7 +1278,7 @@ def f(x): @export def divisors(n: int) -> list[int]: r""" - Computes all positive integer divisors $d$ of the integer $n$ such that $d\ |\ n$. + Computes all positive integer divisors $d$ of the integer $n$ such that $d \mid n$. Arguments: n: An integer. diff --git a/src/galois/typing.py b/src/galois/typing.py index 26ea007f8..0ab6761c9 100644 --- a/src/galois/typing.py +++ b/src/galois/typing.py @@ -54,7 +54,7 @@ # Ascending degrees GF("2 + 2x + x^2") -- :obj:`~galois.Array`: A previously-created scalar :obj:`~galois.Array` object. No coercion is necessary. +- :obj:`~galois.Array`: A previously created scalar :obj:`~galois.Array` object. No coercion is necessary. .. rubric:: Alias """ @@ -115,7 +115,7 @@ x = np.array([[17, 4], [148, 205]]); x GF(x) -- :obj:`~galois.Array`: A previously-created :obj:`~galois.Array` object. No coercion is necessary. +- :obj:`~galois.Array`: A previously created :obj:`~galois.Array` object. No coercion is necessary. .. rubric:: Alias """ @@ -225,7 +225,7 @@ galois.Poly([2, 0, 1], field=GF) galois.Poly(GF([2, 0, 1])) -- :obj:`~galois.Poly`: A previously-created :obj:`~galois.Poly` object. No coercion is necessary. +- :obj:`~galois.Poly`: A previously created :obj:`~galois.Poly` object. No coercion is necessary. .. rubric:: Alias """ diff --git a/tests/fields/test_classes.py b/tests/fields/test_classes.py index b2523ddd7..838517ec8 100644 --- a/tests/fields/test_classes.py +++ b/tests/fields/test_classes.py @@ -95,7 +95,7 @@ def test_cant_set_attribute(attribute): def test_is_primitive_poly(): """ - Verify the `is_primitive_poly` boolean is calculated correctly for fields constructed with explicitly-specified + Verify the `is_primitive_poly` boolean is calculated correctly for fields constructed with explicitly specified irreducible polynomials. """ # GF(2^m) with integer dtype diff --git a/tests/polys/luts/irreducible_polys_min.py b/tests/polys/luts/irreducible_polys_min.py index 454d9de59..ae541f5ef 100644 --- a/tests/polys/luts/irreducible_polys_min.py +++ b/tests/polys/luts/irreducible_polys_min.py @@ -1,5 +1,5 @@ """ -A module containing LUTs for lexicographically-first irreducible polynomials with minimal terms. +A module containing LUTs for lexicographically first irreducible polynomials with minimal terms. LUT items obtained by randomly picking degrees and checking the PDF,