Source code for murasyp.functions

from __future__ import division
from collections import Mapping
from fractions import Fraction

[docs]class Function(Mapping): """Rational-valued functions :type `mapping`: :class:`~collections.Mapping` (such as a :class:`dict`) to a representation of :class:`~numbers.Real` Features: * Values may be specified as :class:`int`, :class:`float`, or :class:`~fractions.Fraction`, whose :class:`str`-representation is then especially convenient. >>> f = Function({'a': 11e-1, 'b': '-1/2','c': 0}) >>> assert f == Function({'a': '11/10', 'c': 0, 'b': '-1/2'}) >>> f['a'] Fraction(11, 10) .. note:: No floats are ever really used; they are immediately converted to fractions and should be seen as just a convenient input representation for decimal numbers. * Scalar multiplication and division, as well as pointwise multiplication, addition, and subtraction (also with scalars) is possible. >>> f = Function({'a': 1.1, 'b': '-1/2','c': 0}) >>> g = Function({'b': '.6', 'c': -2, 'd': 0.0}) >>> assert -1 + (.3 * f - g) / 2 == Function({'c': 0, 'b': '-11/8'}) >>> assert f * g == Function({'c': 0, 'b': '-3/10'}) .. note:: The domain of results of sums and differences is the intersection of the respective domains. """ def __init__(self, mapping={}): """Create a rational-valued function""" if isinstance(mapping, Mapping): self._mapping = {arg: self._make_rational(value) for arg, value in mapping.items()} else: raise TypeError("specify a mapping") def _make_rational(self, value): """Make a Fraction of acceptable input""" if type(value) == float: value = str(value) # treat floats as decimal numbers try: return Fraction(value) except ValueError: print(repr(value) + " is not a Rational number") __len__ = lambda self: len(self._mapping) __iter__ = lambda self: iter(self._mapping) __contains__ = lambda self, element: element in self._mapping __getitem__ = lambda self, element: self._mapping[element] def __repr__(self): """Return a readable unambiguous string representation""" return type(self).__name__ + '(' + str(self) + ')' def __str__(self): """Return a readable string representation""" return ('{' + ', '.join(repr(arg) + ': ' + (repr(str(val)) if '/' in str(val) else str(val)) for arg, val in self._mapping.items()) + '}')
[docs] def domain(self): """Domain of the function :returns: the domain of the function, i.e., those values for which the function is defined :rtype: :class:`frozenset` >>> assert ( ... Function({'a': 1, 'b': -1, 'c': 0}).domain() == ... frozenset({'a', 'c', 'b'}) ... ) """ return frozenset(self.keys())
[docs] def range(self): """Range of the function :returns: the range of the function, i.e., the set of all values returned by the function :rtype: :class:`frozenset` >>> assert ( ... Function({'a': 1, 'b': -1, 'c': 0}).range() == ... frozenset({Fraction(0, 1), Fraction(1, 1), Fraction(-1, 1)}) ... ) """ return frozenset(self.values())
[docs] def support(self): """Support of the function :returns: the support of the function, i.e., that part of the domain for which the function is nonzero :rtype: :class:`frozenset` >>> assert ( ... Function({'a': 1, 'b': -1, 'c': 0}).support() == ... frozenset({'a', 'b'}) ... ) """ return frozenset(arg for arg, value in self.items() if value != 0)
def _with_scalar(self, other, operator): """Application of a binary operator to a function/scalar-pair""" try: other = self._make_rational(other) return type(self)({arg: operator(value, other) for arg, value in self.items()}) except: return NotImplemented def _with_function(self, other, operator): """pointwise application of a binary operator to a pair of functions""" if isinstance(other, type(self)): return type(self)({arg: operator(self[arg], other[arg]) for arg in self._domain_joiner(other)}) else: raise TypeError("cannot apply '" + operator.__name__ + "'" " to objects of types: '" + type(self).__name__ + "' and '" + type(other).__name__ + "'") _domain_joiner = lambda self, other: iter(self.domain() & other.domain()) def _pointwise(self, other, operator): """Pointwise application of a binary operator""" try: return self._with_function(other, operator) except: return self._with_scalar(other, operator) __add__ = lambda self, other: self._pointwise(other, Fraction.__add__) __radd__ = __add__ __mul__ = lambda self, other: self._pointwise(other, Fraction.__mul__) __rmul__ = __mul__ __truediv__ = lambda self, other: self._with_scalar(other, Fraction.__truediv__) __neg__ = lambda self: self * (-1) __sub__ = lambda self, other: self + (-other) __rsub__ = lambda self, other: -(self - other)