Source code for comch.free_module.free_module

from collections import Counter


[docs]class FreeModuleElement(Counter): r"""Element in a free :math:`\mathbb{Z}/n \mathbb{Z}`-module. Let :math:`R` be a ring and :math:`B` a set. The free :math:`R`-module generated by :math:`B` consists of all :math:`R`-linear combination of elements in :math:`B` .. math:: R[B] = \Big\{ \sum_i r_ib_i\ |\ r_i \in R, b_i \in B \Big\}. This class models elements in free :math:`\mathbb Z/n \mathbb Z`-modules with :math:`\mathbb Z/0 \mathbb Z = \mathbb Z`. The ring is specified via the class attribute *torsion* corresponding to the non-negative integer :math:`n`. ATTRIBUTES ---------- torsion : :class:`int` The non-neg int :math:`n` of the ring :math:`\mathbb Z/n \mathbb Z`. """ default_torsion = 0 """Class attribute: Used if :attr:`torsion` is ``None`` during initialization."""
[docs] def __init__(self, data=None, torsion=None): """Initializes *self*. PARAMETERS ---------- data : ``dict`` Dictionary representing a linear combination of basis elements. Items in the dict correspond to pairs (basis_element: coefficient). torsion : :class:`int` The non-neg int :math:`n` of the ring :math:`\mathbb Z/n \mathbb Z`. EXAMPLE ------- >>> print(FreeModuleElement()) 0 >>> print(FreeModuleElement({'a': 1, 'b': -1, 'c': 0})) a - b """ if torsion is None: torsion = type(self).default_torsion self.torsion = torsion super(FreeModuleElement, self).__init__(data) self.preferred_rep()
def __hash__(self): return hash(frozenset(self)) def __str__(self): """Coefficient first representation.""" if not self: return '0' else: answer = '' for key, value in self.items(): if value < -1: answer += f'- {abs(value)}{key} ' elif value == -1: answer += f'- {key} ' elif value == 1: answer += f'+ {key} ' elif value > 1: answer += f'+ {value}{key} ' if answer[0] == '+': answer = answer[2:] return answer[:-1]
[docs] def __add__(self, other): """Addition: *self* + *other*. PARAMETERS ---------- other : :class:`comch.free_module.FreeModuleElement` object The element to add to *self*. RETURNS ------- :class:`comch.free_module.FreeModuleElement` object The sum of *self* and *other*. EXAMPLE ------- >>> FreeModuleElement({'a': 1, 'b': 2}) + FreeModuleElement({'a': 1}) FreeModuleElement({'a': 2, 'b': 2}) """ if self.torsion != other.torsion: raise TypeError('only defined for equal attribute torsion') answer = self.create(self) answer.update(other) answer.preferred_rep() return answer
[docs] def __sub__(self, other): """Diference: *self* - *other*. PARAMETERS ---------- other : :class:`comch.free_module.FreeModuleElement` object The element to subtract from *self*. RETURNS ------- :class:`comch.free_module.FreeModuleElement` object The difference of *self* and *other*. EXAMPLE ------- >>> FreeModuleElement({'a': 1, 'b': 2}) - FreeModuleElement({'a': 1}) FreeModuleElement({'b': 2}) """ if self.torsion != other.torsion: raise TypeError('only defined for equal attribute torsion') answer = self.create(self) answer.subtract(other) answer.preferred_rep() return answer
[docs] def __rmul__(self, c): """Left action: *c* * *self*. PARAMETERS ---------- c : int The element to act *self* with. RETURNS ------- :class:`comch.free_module.FreeModuleElement` object The action of *c* on *self*. EXAMPLE ------- >>> 3 * FreeModuleElement({'a':1, 'b':2}) FreeModuleElement({'b': 6, 'a': 3}) """ if not isinstance(c, int): raise TypeError(f'Act only by int not by type {type(c)}') scaled = {k: c * v for k, v in self.items()} return self.create(scaled)
[docs] def __truediv__(self, c): """Division of *self* by *c*. When c has a multiplicative inverse in the ground ring, divides *self* by *c*. PARAMETERS ---------- c : int The element to divide *self* by. RETURNS ------- :class:`comch.free_module.FreeModuleElement` object The action of *1/c* on *self*. EXAMPLE ------- >>> FreeModuleElement({'a': 1, 'b': 2}, torsion=5) / 3 FreeModuleElement({'b': 4, 'a': 2}) """ if not isinstance(c, int): raise TypeError(f'Act only by int not by type {type(c)}') if self.torsion == 0: raise TypeError(f'Division defined for positive torsion only') inv = pow(c, -1, self.torsion) scaled = {k: inv * v for k, v in self.items()} return self.create(scaled)
[docs] def __neg__(self): """Additive inverse: - *self*. RETURNS ------- :class:`comch.free_module.FreeModuleElement` object The additive inverse of *self*. EXAMPLE ------- >>> - FreeModuleElement({'a': 1, 'b': 2}) FreeModuleElement({'a': -1, 'b': -2}) """ return self.__rmul__(-1)
[docs] def __iadd__(self, other): """In place addition: *self* = *self* + *other*. PARAMETERS ---------- other : :class:`comch.free_module.FreeModuleElement` object The element to add to *self*. EXAMPLE ------- >>> x = FreeModuleElement({'a': 1, 'b': 2}) >>> x += FreeModuleElement({'a': 3, 'b': 6}) >>> x FreeModuleElement({'b': 8, 'a': 4}) """ if self.torsion != other.torsion: raise TypeError('only defined for equal attribute torsion') self.update(other) self.preferred_rep() return self
[docs] def __isub__(self, other): """In place difference: *self* = *self* - *other*. PARAMETERS ---------- other : :class:`comch.free_module.FreeModuleElement` object The element to subtract from *self*. EXAMPLE ------- >>> x = FreeModuleElement({'a': 1, 'b': 2}) >>> x -= FreeModuleElement({'a': 3, 'b': 6}) >>> x FreeModuleElement({'a': -2, 'b': -4}) """ if self.torsion != other.torsion: raise TypeError('only defined for equal attribute torsion') self.subtract(other) self.preferred_rep() return self
[docs] def preferred_rep(self): r"""The preferred representative of *self*. Consisting of pairs `basis_element: coefficient` with `coefficient` different from 0 and in the set :math:`\{1, \dots, r-1\}` if :attr:`torsion` is an :class:`int` denoted :math:`r`. EXAMPLE ------- >>> FreeModuleElement({'a': 1, 'b': 2, 'c': 0}) FreeModuleElement({'b': 2, 'a': 1}) """ # reducing coefficients mod torsion if self.torsion != 0: for key, value in self.items(): self[key] = value % self.torsion # removing key:value pairs with value = 0 zeros = [k for k, v in self.items() if not v] for key in zeros: del self[key]
[docs] def set_torsion(self, torsion): """Sets the torsion of *self*. PARAMETERS ---------- torsion : :class:`int` The new `torsion` of *self* EXAMPLE ------- >>> FreeModuleElement({'a': 1, 'b': 2}).set_torsion(2) FreeModuleElement({'a': 1}) """ setattr(self, 'torsion', torsion) self.preferred_rep() return self
[docs] def create(self, other=None): """Initializes with the same type and attribute values as *self*. PARAMETERS ---------- other : dict or None, default: ``None`` Data to be initialized. RETURNS ------- type(*self*) object The initialized object with the given data EXAMPLE ------- >>> x = FreeModuleElement({'a': 1}) >>> x + x.create({'b': 1}) FreeModuleElement({'a': 1, 'b': 1}) """ answer = type(self)(other) answer.__dict__ = self.__dict__ answer.preferred_rep() return answer
[docs] def zero(self): """Initializes 0 with same type and attribute values as *self*. RETURNS ------- type(*self*) object The initialized empty object EXAMPLE ------- >>> x = FreeModuleElement({'a': 1}) >>> x + x.zero() == x True """ return self.create()