Source code for galois.gf2

import numba
import numpy as np

from .gf import _GF


@numba.vectorize(["uint8(uint8, uint8)", "uint8(uint8, int64)"], nopython=True)
def _numba_ufunc_power(a, b):
    # Calculate a**b
    if a == 0:
        return 0
    elif b == 0:
        return 1
    else:
        return a


[docs]class GF2(_GF): """ asdf Examples -------- GF2 class properties .. ipython:: python print(galois.GF2) galois.GF2.characteristic galois.GF2.power galois.GF2.order galois.GF2.prim_poly Construct arrays in GF2 .. ipython:: python a = galois.GF2([1,0,1,1]); a b = galois.GF2([1,1,1,1]); b Arithmetic with GF2 arrays .. ipython:: python # Element-wise addition a + b # Element-wise subtraction a - b # Element-wise multiplication a * b # Element-wise division a / b """ characteristic = 2 power = 1 order = 2 prim_poly = None alpha = 1 _dtype = np.uint8 def _add(self, ufunc, method, inputs, kwargs, meta): """ In GF(2), 0+1=1 and 1+1=0, which is equivalent to bitwise XOR. Subtraction is the same as addition in GF(2) because each element is its additive inverse. """ self._ufunc_verify_input_in_field(ufunc, inputs[0]) self._ufunc_verify_input_in_field(ufunc, inputs[1]) outputs = getattr(np.bitwise_xor, method)(*inputs, **kwargs) return outputs def _subtract(self, ufunc, method, inputs, kwargs, meta): return self._add(ufunc, method, inputs, kwargs, meta) def _multiply(self, ufunc, method, inputs, kwargs, meta): """ In GF(2), 0*a=0 for any a >= 0 and 1*a=1 for any a > 0, which is equivalent to bitwise AND. """ for i in [0,1]: if i in meta["gf_inputs"]: self._ufunc_verify_input_in_field(ufunc, inputs[i]) else: self._ufunc_verify_input_in_integers(ufunc, inputs[i]) outputs = getattr(np.bitwise_and, method)(*inputs, casting="unsafe", **kwargs) return outputs def _divide(self, ufunc, method, inputs, kwargs, meta): """ In GF(2), 0/1=0 and 1/1=1, which is equivalent to bitwise AND. """ assert np.count_nonzero(inputs[1]) == inputs[1].size, "Cannont divide by 0 in a Galois field" self._ufunc_verify_input_in_field(ufunc, inputs[0]) self._ufunc_verify_input_in_field(ufunc, inputs[1]) # if np.count_nonzero(inputs[1]) != inputs[1].size: # warnings.warn("divide by zero encountered in \"{}\", 0 is outputted where 'Inf' would otherwise".format(ufunc), RuntimeWarning) outputs = getattr(np.bitwise_and, method)(*inputs, casting="unsafe", **kwargs) return outputs def _negative(self, ufunc, method, inputs, kwargs, meta): """ In GF(2), each element is its additive inverse. """ outputs = inputs[0] return outputs def _power(self, ufunc, method, inputs, kwargs, meta): """ In GF(2), a^0=1 and a^b=a for any a,b in GF(2). We created a special numba-optimized ufunc to perform this operation on all elements in the two input arrays a,b. """ assert not np.any(np.logical_and(inputs[0] == 0, inputs[1] < 0)), "Cannot exponentiate 0 to a negative power in a Galois field" self._ufunc_verify_input_in_field(ufunc, inputs[0]) self._ufunc_verify_input_in_integers(ufunc, inputs[1]) # if np.any(np.logical_and(inputs[0] == 0, inputs[1] < 0)): # warnings.warn("divide by zero encountered in \"{}\", 0 is outputted where 'Inf' would otherwise be".format(ufunc), RuntimeWarning) inputs[1] = np.mod(inputs[1], self.order - 1) # x^q = x, where q is the order inputs[1] = inputs[1].astype(self._dtype) outputs = getattr(_numba_ufunc_power, method)(*inputs, **kwargs) return outputs def _square(self, ufunc, method, inputs, kwargs, meta): """ In GF(2), 0^2=0 and 1^2=1. """ outputs = inputs[0] return outputs def _log(self, ufunc, method, inputs, kwargs, meta): """ Log base alpha of elements in GF(2). In GF(2), log(1) = 0 and log(0) is undefined. """ assert np.count_nonzero(inputs[0]) == inputs[0].size, "Cannont compute log(0) in a Galois field" outputs = getattr(np.multiply, method)(*inputs, 0, **kwargs) return outputs