Skip to content

Commit

Permalink
Move ArrayMeta to _meta.py
Browse files Browse the repository at this point in the history
  • Loading branch information
mhostetter committed Apr 27, 2022
1 parent 03919da commit 97a391d
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 115 deletions.
114 changes: 4 additions & 110 deletions galois/_domains/_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
from __future__ import annotations

import contextlib
import inspect
import random
from typing import List, Sequence, Optional, Union, Type
from typing import Sequence, Optional, Union, Type
from typing_extensions import Literal

import numpy as np

from .._overrides import set_module, SPHINX_BUILD

from ._meta import ArrayMeta

__all__ = ["Array"]


Expand Down Expand Up @@ -175,115 +176,8 @@
"""


DTYPES = [np.uint8, np.uint16, np.uint32, np.int8, np.int16, np.int32, np.int64]


class Meta(type):
"""
A metaclass that provides class properties for `Array` subclasses.
"""
# pylint: disable=no-value-for-parameter

def __new__(cls, name, bases, namespace, **kwargs): # pylint: disable=unused-argument
return super().__new__(cls, name, bases, namespace)

def __init__(cls, name, bases, namespace, **kwargs):
super().__init__(name, bases, namespace, **kwargs)
cls._characteristic: int = kwargs.get("characteristic", 0)
cls._degree: int = kwargs.get("degree", 1)
cls._order: int = kwargs.get("order", 0)
cls._dtypes = cls._determine_dtypes()

if cls._dtypes == [np.object_]:
cls._default_ufunc_mode = "python-calculate"
cls._ufunc_modes = ["python-calculate"]
elif cls._order <= 2**20:
cls._default_ufunc_mode = "jit-lookup"
cls._ufunc_modes = ["jit-lookup", "jit-calculate"]
else:
cls._default_ufunc_mode = "jit-calculate"
cls._ufunc_modes = ["jit-lookup", "jit-calculate"]
cls._ufunc_mode = None # This is set in the first call to compile

cls._name = "" # Needs overridden

# A dictionary of ufuncs and LUTs
cls._ufuncs = {}
cls._EXP = np.array([], dtype=cls._dtypes[-1])
cls._LOG = np.array([], dtype=cls._dtypes[-1])
cls._ZECH_LOG = np.array([], dtype=cls._dtypes[-1])
cls._ZECH_E = 0

cls._functions = {}

# Class variables needed when displaying elements with fixed width
cls._display_mode = kwargs.get("display", "int")
cls._element_fixed_width = None
cls._element_fixed_width_counter = 0

# By default, verify array elements are within the valid range when `.view()` casting
cls._verify_on_view = True

def __repr__(cls) -> str:
return f"<class 'numpy.ndarray over {cls._name}'>"

def __str__(cls) -> str:
string = "Domain:"
string += f"\n name: {cls._name}"
string += f"\n characteristic: {cls._characteristic}"
string += f"\n degree: {cls._degree}"
string += f"\n order: {cls._order}"

return string

def __dir__(cls) -> List[str]:
"""
Add class properties from the metaclass onto the new Array class's dir().
"""
metacls = type(cls)
classproperties = [item for item in dir(metacls) if item[0] != "_" and inspect.isdatadescriptor(getattr(metacls, item))]
return sorted(list(super().__dir__()) + classproperties)

###############################################################################
# Helper methods
###############################################################################

def _determine_dtypes(cls) -> List[np.dtype]:
"""
Determine which NumPy integer data types are valid for this finite field. At a minimum, valid dtypes are ones that
can hold x for x in [0, order).
"""
dtypes = [dtype for dtype in DTYPES if np.iinfo(dtype).max >= cls._order - 1]
if len(dtypes) == 0:
dtypes = [np.object_]
return dtypes

###############################################################################
# View methods
###############################################################################

@contextlib.contextmanager
def _view_without_verification(cls):
"""
A context manager to disable verifying array element values are within [0, p^m). For internal library use only.
"""
prev_value = cls._verify_on_view
cls._verify_on_view = False
yield
cls._verify_on_view = prev_value

def _view(cls, array: np.ndarray) -> Array:
"""
View the input array to the FieldArray subclass using the `_view_without_verification()` context manager. This disables
bounds checking on the array elements. Instead of `x.view(field)` use `field._view(x)`.
"""
with cls._view_without_verification():
array = array.view(cls)
return array


@set_module("galois")
class Array(np.ndarray, metaclass=Meta):
class Array(np.ndarray, metaclass=ArrayMeta):
r"""
A :obj:`~numpy.ndarray` subclass over a Galois field or Galois ring.
Expand Down
113 changes: 113 additions & 0 deletions galois/_domains/_meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from __future__ import annotations

import contextlib
import inspect
from typing import List

import numpy as np

DTYPES = [np.uint8, np.uint16, np.uint32, np.int8, np.int16, np.int32, np.int64]


class ArrayMeta(type):
"""
A metaclass that provides class properties for `Array` subclasses.
"""
# pylint: disable=no-value-for-parameter

def __new__(cls, name, bases, namespace, **kwargs): # pylint: disable=unused-argument
return super().__new__(cls, name, bases, namespace)

def __init__(cls, name, bases, namespace, **kwargs):
super().__init__(name, bases, namespace, **kwargs)
cls._characteristic: int = kwargs.get("characteristic", 0)
cls._degree: int = kwargs.get("degree", 1)
cls._order: int = kwargs.get("order", 0)
cls._dtypes = cls._determine_dtypes()

if cls._dtypes == [np.object_]:
cls._default_ufunc_mode = "python-calculate"
cls._ufunc_modes = ["python-calculate"]
elif cls._order <= 2**20:
cls._default_ufunc_mode = "jit-lookup"
cls._ufunc_modes = ["jit-lookup", "jit-calculate"]
else:
cls._default_ufunc_mode = "jit-calculate"
cls._ufunc_modes = ["jit-lookup", "jit-calculate"]
cls._ufunc_mode = None # This is set in the first call to compile

cls._name = "" # Needs overridden

# A dictionary of ufuncs and LUTs
cls._ufuncs = {}
cls._EXP = np.array([], dtype=cls._dtypes[-1])
cls._LOG = np.array([], dtype=cls._dtypes[-1])
cls._ZECH_LOG = np.array([], dtype=cls._dtypes[-1])
cls._ZECH_E = 0

cls._functions = {}

# Class variables needed when displaying elements with fixed width
cls._display_mode = kwargs.get("display", "int")
cls._element_fixed_width = None
cls._element_fixed_width_counter = 0

# By default, verify array elements are within the valid range when `.view()` casting
cls._verify_on_view = True

def __repr__(cls) -> str:
return f"<class 'numpy.ndarray over {cls._name}'>"

def __str__(cls) -> str:
string = "Domain:"
string += f"\n name: {cls._name}"
string += f"\n characteristic: {cls._characteristic}"
string += f"\n degree: {cls._degree}"
string += f"\n order: {cls._order}"

return string

def __dir__(cls) -> List[str]:
"""
Add class properties from the metaclass onto the new Array class's dir().
"""
metacls = type(cls)
classproperties = [item for item in dir(metacls) if item[0] != "_" and inspect.isdatadescriptor(getattr(metacls, item))]
return sorted(list(super().__dir__()) + classproperties)

###############################################################################
# Helper methods
###############################################################################

def _determine_dtypes(cls) -> List[np.dtype]:
"""
Determine which NumPy integer data types are valid for this finite field. At a minimum, valid dtypes are ones that
can hold x for x in [0, order).
"""
dtypes = [dtype for dtype in DTYPES if np.iinfo(dtype).max >= cls._order - 1]
if len(dtypes) == 0:
dtypes = [np.object_]
return dtypes

###############################################################################
# View methods
###############################################################################

@contextlib.contextmanager
def _view_without_verification(cls):
"""
A context manager to disable verifying array element values are within [0, p^m). For internal library use only.
"""
prev_value = cls._verify_on_view
cls._verify_on_view = False
yield
cls._verify_on_view = prev_value

def _view(cls, array: np.ndarray) -> "Array":
"""
View the input array to the FieldArray subclass using the `_view_without_verification()` context manager. This disables
bounds checking on the array elements. Instead of `x.view(field)` use `field._view(x)`.
"""
with cls._view_without_verification():
array = array.view(cls)
return array
2 changes: 1 addition & 1 deletion galois/_fields/_gfp.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import numba
import numpy as np

from .._domains._array import DTYPES
from .._domains._meta import DTYPES

from ._array import FieldArray

Expand Down
2 changes: 1 addition & 1 deletion galois/_fields/_gfpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import numba
import numpy as np

from .._domains._array import DTYPES
from .._domains._meta import DTYPES

from ._array import FieldArray

Expand Down
2 changes: 1 addition & 1 deletion galois/_fields/_linalg.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
import numpy as np

from .._domains._array import DTYPES
from .._domains._meta import DTYPES


def _lapack_linalg(a, b, function, out=None, n_sum=None):
Expand Down
4 changes: 2 additions & 2 deletions galois/_fields/_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

import numpy as np

from .._domains._array import Meta
from .._domains._meta import ArrayMeta
from .._modular import totatives
from .._polys import Poly
from .._polys._conversions import integer_to_poly, poly_to_str


class FieldArrayMeta(Meta):
class FieldArrayMeta(ArrayMeta):
"""
A metaclass that provides documented class properties for `FieldArray` subclasses.
"""
Expand Down

0 comments on commit 97a391d

Please sign in to comment.