r"""
Tests for GF(2) linear algebra
==============================
The ``linear`` module defines functions that
test for linearity of functions defined on
GF(2) vector spaces.
AUTHORS:
- Paul Leopardi (2023-01-16): initial version
"""
from sage.matrix.constructor import Matrix
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
from sage.rings.integer import Integer
from boolean_cayley_graphs.integer_bits import base2
encoding = "UTF-8"
def is_linear(dim, perm, certificate=False):
r"""
Check if a permutation on `range(2**dim)` is linear on `GF(2)**dim`.
INPUT:
- ``dim`` -- the dimension of the GF(2) linear space.
- ``perm`` -- a function from `range(2**dim)` to iself, usually a permutation.
- ``certificate`` -- bool (default False). If true, return the GF(2) matrix
that corresponds to the permutation.
OUTPUT:
If ``certificate`` is false, a bool value.
If ``certificate`` is true, a tuple consisting of either (False, None)
or (True, M), where M is a GF(2) matrix that corresponds to the permutation.
EXAMPLES:
::
sage: from boolean_cayley_graphs.linear import is_linear
sage: dim = 2
sage: perm1 = lambda x: x*3 % 4
sage: is_linear(dim, perm1)
True
sage: is_linear(dim, perm1, certificate=True)
(
[1 0]
True, [1 1]
)
sage: perm2 = lambda x: (x+1) % 4
sage: is_linear(dim, perm2)
False
sage: is_linear(dim, perm2, certificate=True)
(False, None)
"""
v = 2**dim
if perm(0) != 0:
return (False, None) if certificate else False
possibly_linear = True
for a in range(dim):
for b in range(a + 1, dim):
if perm(2**a) ^ perm(2**b) != perm((2**a) ^ (2**b)):
possibly_linear = False
break
if not possibly_linear:
break
if not possibly_linear:
return (False, None) if certificate else False
perm_matrix = Matrix(GF(2), [
base2(dim, Integer(perm(2**a)))
for a in range(dim)]).transpose()
vm = Matrix(GF(2), [
base2(dim, Integer(x))
for x in range(v)]).transpose()
linear = perm_matrix * vm == vm[:, [perm(x) for x in range(v)]]
if certificate:
return (True, perm_matrix) if linear else (False, None)
else:
return linear