Skip to content

Commit

Permalink
Direct GF2-poly; minor numpy; gmpy2.prev_prime().
Browse files Browse the repository at this point in the history
  • Loading branch information
lschoe authored Jan 28, 2024
1 parent af58e2c commit 56cc30c
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 38 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ jobs:
- MPYC_NOPRSS=1
install: pip install numpy==1.23.* gmpy2 uvloop
- python: pypy3.9-7.3.9
install: pip install numpy==1.24.*
install: pip install numpy==1.25.*
- python: 3.10
env:
- MPYC_NONUMPY=1
- MPYC_NOGMPY=1
- MPYC_NOUVLOOP=1
- python: 3.11
install: pip install numpy==1.25.* gmpy2
install: pip install numpy==1.24.* gmpy2
- python: 3.12
install: pip install numpy uvloop
before_install:
Expand Down
4 changes: 4 additions & 0 deletions docs/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ To enhance the performance of MPyC with faster integer arithmetic, install the
`gmpy2 <https://gmpy2.readthedocs.io>`_ package, e.g.,
using ``pip install gmpy2``.

To enhance the performance of MPyC with an alternative event loop, install the
`uvloop <https://uvloop.readthedocs.io>`_ package, e.g.,
using ``pip install uvloop`` on Mac/Linux and currently ``pip install winloop`` on Windows.

PyPy / Nuitka
-------------

Expand Down
23 changes: 10 additions & 13 deletions mpyc/finfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,8 +718,8 @@ def __init__(self, value, check=True, copy=False):
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
# TODO: make more general
cls = type(self)
if any(isinstance(a, np.ndarray) and a.dtype != object and a.dtype not in cls._mix_types
for a in inputs):
if any(isinstance(a, np.ndarray) and a.dtype != object
and not issubclass(a.dtype.type, cls._mix_types) for a in inputs):
return NotImplemented

if ufunc.__name__ == 'equal':
Expand Down Expand Up @@ -1358,9 +1358,7 @@ class PrimeFieldArray(FiniteFieldArray):

_mix_types = int
if np:
_mix_types = (int, np.int64, np.int32, np.int16, np.int8,
np.uint64, np.uint32, np.uint16, np.uint8)
# TODO: consider use of np.integer
_mix_types = (int, np.integer)

@classmethod
def intarray(cls, a):
Expand All @@ -1383,10 +1381,11 @@ def __abs__(self):
def signed_(self):
"""Return signed integer representation, symmetric around zero."""
p = self.field.modulus
f = np.vectorize(lambda a: a - p if a > p>>1 else a, otypes='O')
return f(self.value)
# TODO: check following alternative, issue with np.where result containing floats
# return self.value - np.where(self.value > p>>1, p, 0)
return np.where(self.value > p>>1, self.value - p, self.value)
# NB: avoiding np.where(..., p, 0) which causes issues with floats, as
# np.result_type(1 << 63, 0) returns dtype('float64'), but
# np.result_type(1 << 62, 0) returns dtype('int64'), and
# np.result_type(1 << 64, 0) returns dtype('O').

def unsigned_(self):
"""Return unsigned integer representation."""
Expand Down Expand Up @@ -1467,8 +1466,7 @@ class ExtensionFieldArray(FiniteFieldArray):

_mix_types = (int, gfpx.Polynomial)
if np:
_mix_types += (np.int64, np.int32, np.int16, np.int8,
np.uint64, np.uint32, np.uint16, np.uint8)
_mix_types += (np.integer,)

@classmethod
def _pow(cls, a, b): # NB: exponents assumed to be integer(s)
Expand Down Expand Up @@ -1534,8 +1532,7 @@ class BinaryFieldArray(ExtensionFieldArray):

_mix_types = (int, gfpx.BinaryPolynomial)
if np:
_mix_types += (np.int64, np.int32, np.int16, np.int8,
np.uint64, np.uint32, np.uint16, np.uint8)
_mix_types += (np.integer,)

@classmethod
def _is_sqr(cls, a):
Expand Down
9 changes: 6 additions & 3 deletions mpyc/gfpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ def GFpX(p):
if not gmpy2.is_prime(p):
raise ValueError('number is not prime')

BasePolynomial = BinaryPolynomial if p == 2 else Polynomial
GFpPolynomial = type(f'GF({p})[{X}]', (BasePolynomial,), {'__slots__': ()})
GFpPolynomial.p = p
if p == 2:
GFpPolynomial = BinaryPolynomial
GFpPolynomial.__name__ = f'GF({p})[{X}]'
else:
GFpPolynomial = type(f'GF({p})[{X}]', (Polynomial,), {'__slots__': ()})
GFpPolynomial.p = p
globals()[f'GF({p})[{X}]'] = GFpPolynomial # NB: exploit unique name dynamic Polynomial type
return GFpPolynomial

Expand Down
31 changes: 18 additions & 13 deletions mpyc/gmpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,24 @@ def ratrec(x, y, N=None, D=None):
raise ValueError('rational reconstruction not possible')


def prev_prime(x):
"""Return the greatest probable prime number < x, if any."""
# TODO: future releases of gmpy2 will support this function (using GMP 6.3+)
if x <= 2:
raise ValueError(f'no smaller prime')

if x == 3:
return 2

x -= 1 + x%2
while not is_prime(x):
x -= 2
return x
try:
if os.getenv('MPYC_NOGMPY') == '1':
raise ImportError # stub for gmpy2.prev_prime() will be loaded

from gmpy2 import prev_prime # only available from gmpy2 2.2+ based on GMP 6.3+
except ImportError:
def prev_prime(x):
"""Return the greatest probable prime number < x, if any."""
if x < 3:
raise ValueError('x must be >= 3')

if x == 3:
return 2

x -= 1 + x%2
while not is_prime(x):
x -= 2
return x


try:
Expand Down
9 changes: 2 additions & 7 deletions tests/test_gfpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ class Arithmetic(unittest.TestCase):

def setUp(self):
gf2x = gfpx.GFpX(2)
gf2x_ = gfpx.BinaryPolynomial
gf3x = gfpx.GFpX(3)
gf101x = gfpx.GFpX(101)
self.mod_2 = (gf2x, gf2x_)
self.mod_all = self.mod_2 + (gf3x, gf101x)
self.mod_all = (gf2x, gf3x, gf101x)

def test_modall(self):
self.assertRaises(ValueError, gfpx.GFpX, 4)
Expand Down Expand Up @@ -95,10 +93,7 @@ def _test_errors(self, poly):
self.assertRaises(ValueError, operator.pow, poly(3), -16)

def test_mod2(self):
for poly in self.mod_2:
self._test_mod2(poly)

def _test_mod2(self, poly):
poly = gfpx.GFpX(2)
self.assertEqual(poly([0, 1, 1]), f'{X}^2+{X}')
self.assertEqual(poly._to_list(6), [0, 1, 1])
self.assertEqual(poly.from_terms(f'{X}+{X}'), 0)
Expand Down

0 comments on commit 56cc30c

Please sign in to comment.