From 259334333220a236e253a991bc743e1646c59cfb Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 16 Jun 2005 21:55:52 +0000 Subject: [PATCH] [pymbolic @ Arch-1:inform@tiker.net--iam-2005%pymbolic--mainline--1.0--patch-10] Build more. --- LITERATURE | 1 + TODO | 2 + src/conversion.py | 8 +++ src/mapper/evaluator.py | 4 +- src/mapper/mapper.py | 63 +++------------------- src/operation.py | 26 +++++++++ src/polynomial.py | 117 ++++++++++++++++++++++++++++++++++++---- src/primitives.py | 55 ++++++++++++++++--- src/rational.py | 19 +++++++ src/tests.py | 11 ---- src/traits.py | 42 +++++++++++++++ 11 files changed, 264 insertions(+), 84 deletions(-) create mode 100644 LITERATURE create mode 100644 TODO create mode 100644 src/conversion.py create mode 100644 src/operation.py create mode 100644 src/rational.py delete mode 100644 src/tests.py create mode 100644 src/traits.py diff --git a/LITERATURE b/LITERATURE new file mode 100644 index 0000000..9327269 --- /dev/null +++ b/LITERATURE @@ -0,0 +1 @@ +[Bosch]: S. Bosch, Algebra, 3rd Edition, Springer, 1999 diff --git a/TODO b/TODO new file mode 100644 index 0000000..2426b28 --- /dev/null +++ b/TODO @@ -0,0 +1,2 @@ +Build out traits +Fix mappers wrt rationals, polynomials diff --git a/src/conversion.py b/src/conversion.py new file mode 100644 index 0000000..c84ded2 --- /dev/null +++ b/src/conversion.py @@ -0,0 +1,8 @@ +def to_polynomial(x): + raise NotImplementedError + +def to_expression(x): + raise NotImplementedError + +def to_fraction(x, approx_bits=64-4): + raise NotImplementedError diff --git a/src/mapper/evaluator.py b/src/mapper/evaluator.py index 524f66d..068c7d4 100644 --- a/src/mapper/evaluator.py +++ b/src/mapper/evaluator.py @@ -28,8 +28,8 @@ class EvaluationMapper: result *= child.invoke_mapper(self) return result - def map_quotient(self, expr): - return expr.Child1.invoke_mapper(self) / expr.Child2.invoke_mapper(self) + def map_rational(self, expr): + return expr.numerator.invoke_mapper(self) / expr.denominator.invoke_mapper(self) def map_power(self, expr): return expr.Child1.invoke_mapper(self) ** expr.Child2.invoke_mapper(self) diff --git a/src/mapper/mapper.py b/src/mapper/mapper.py index d126fa0..3175a72 100644 --- a/src/mapper/mapper.py +++ b/src/mapper/mapper.py @@ -8,9 +8,6 @@ class ByArityMapper: def map_negation(self, expr): return self.map_unary(expr) - def map_quotient(self, expr): - return self.map_binary(expr) - def map_power(self, expr): return self.map_binary(expr) @@ -31,6 +28,10 @@ class CombineMapper(ByArityMapper): return self.combine((expr.Child1.invoke_mapper(self), expr.Child2.invoke_mapper(self))) + def map_rational(self, expr): + return self.combine((expr.numerator.invoke_mapper(self), + expr.denominator.invoke_mapper(self))) + def map_n_ary(self, expr): return self.combine(child.invoke_mapper(self) for child in expr.Children) @@ -48,58 +49,6 @@ class CombineMapper(ByArityMapper): -class EvaluationMapper: - def __init__(self, context): - self.Context = context - - def map_constant(self, expr): - return expr.Value - - def map_variable(self, expr): - return self.Context[expr.Name] - - def map_call(self, expr): - return expr.Function.invoke_mapper(self)( - *[par.invoke_mapper(self) - for par in expr.Parameters]) - - def map_sum(self, expr): - return sum(child.invoke_mapper(self) - for child in expr.Children) - - def map_negation(self, expr): - return -expr.Child.invoke_mapper(self) - - def map_product(self, expr): - if len(expr.Children) == 0: - return 1 - result = expr.Children[0].invoke_mapper(self) - for child in expr.Children[1:]: - result *= child.invoke_mapper(self) - return result - - def map_quotient(self, expr): - return expr.Child1.invoke_mapper(self) / expr.Child2.invoke_mapper(self) - - def map_power(self, expr): - return expr.Child1.invoke_mapper(self) ** expr.Child2.invoke_mapper(self) - - def map_polynomial(self, expr): - if len(expr.Children) == 0: - return 0 - result = expr.Children[-1].invoke_mapper(self) - b_ev = expr.Base.invoke_mapper(self) - for child in expr.Children[-2::-1]: - result = result * b_ev + child.invoke_mapper(self) - return result - - def map_list(self, expr): - return [child.invoke_mapper(self) for child in expr.Children] - - - - - class IdentityMapper(ByArityMapper): def map_unary(self, expr): return expr.__class__(expr.Child.invoke_mapper(self)) @@ -111,6 +60,10 @@ class IdentityMapper(ByArityMapper): def map_n_ary(self, expr): return expr.__class__(tuple(child.invoke_mapper(self) for child in expr.Children)) + + def map_quotient(self, expr): + return expr.__class__(expr.numerator.invoke_mapper(self), + expr.denominator.invoke_mapper(self)) def map_constant(self, expr): return expr diff --git a/src/operation.py b/src/operation.py new file mode 100644 index 0000000..d3e752f --- /dev/null +++ b/src/operation.py @@ -0,0 +1,26 @@ +def degree(x): + if isinstance(x, Polynomial): + return + + + + +def gcd_extended(x, y): + + + + + +def gcd(x, y): + raise NotImplementedError + + + + +def lcm_extended(x, y): + raise NotImplementedError + + + +def lcm(x, y): + raise NotImplementedError diff --git a/src/polynomial.py b/src/polynomial.py index d455059..6565a5d 100644 --- a/src/polynomial.py +++ b/src/polynomial.py @@ -1,11 +1,16 @@ -class Polynomial(Expression): - def __init__(self, base, coeff): +import tests + + + + +class Polynomial(object): + def __init__(self, base, data): self.Base = base # list of (exponent, coefficient tuples) # sorted in increasing order # one entry per degree - self.Data = children + self.Data = data # Remember the Zen, Luke: Sparse is better than dense. @@ -16,7 +21,7 @@ class Polynomial(Expression): def __add__(self, other): if not isinstance(other, Polynomial) or other.Base != self.Base: - return Expression.__add__(self, other) + other = Polynomial(other, ((1,1),)) iself = 0 iother = 0 @@ -27,7 +32,7 @@ class Polynomial(Expression): exp_other = other.Data[iother][0] if exp_self == exp_other: coeff = self.Data[iself][1] + other.Data[iother][1] - if coeff != Constant(0): + if not coeff: result.append((exp_self, coeff)) iself += 1 iother += 1 @@ -49,15 +54,107 @@ class Polynomial(Expression): result.append((exp_other, other.Data[iother][1])) iother += 1 - return Polynomial(self.Base, result) + return Polynomial(self.Base, tuple(result)) + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + return self+(-other) + + def __rsub__(self, other): + return (-other)+self def __mul__(self, other): if not isinstance(other, Polynomial) or other.Base != self.Base: - return Expression.__mul__(self, other) - raise NotImplementedError + return Polynomial(self.Base, [(exp, coeff * other) + for exp, coeff in self.Data]) + + result = [] + + for s_exp, s_coeff in self.Data: + for o_exp, o_coeff in other.Data: + result.append((s_exp+o_exp, s_coeff*o_coeff)) + + def sortkey((exp, coeff)): return exp + result.sort(key=sortkey) + + uniq_result = [] + i = 0 + last_exp = None + for exp, coeff in result: + if last_exp == exp: + newcoeff = uniq_result[-1][1]+coeff + if newcoeff: + uniq_result.pop() + else: + uniq_result[-1] = last_exp, newcoeff + + else: + uniq_result.append((exp, coeff)) + last_exp = exp + return Polynomial(self.Base, tuple(uniq_result)) + + def __rmul__(self, other): + return Polynomial(self.Base, [(exp, other * coeff) + for exp, coeff in self.Data]) + + def __pow__(self, other): + n = int(other) + if n < 0: + raise RuntimeError, "negative powers of polynomials not defined" + + aux = Polynomial(self.Base, ((0, 1),)) + x = self + + # http://c2.com/cgi/wiki?IntegerPowerAlgorithm + while n > 0: + if n & 1: + aux *= x + if n == 1: + return aux + x = x * x + n //= 2 + + return aux + + def __divmod__(self, other): + pass + + def __str__(self): + sbase = str(self.Base) + def stringify_expcoeff((exp, coeff)): + if exp == 1: + if tests.is_one(coeff): + return sbase + else: + return "%s*%s" % (str(coeff), sbase) + elif exp == 0: + return str(coeff) + else: + if tests.is_one(coeff): + return "%s**%s" % (sbase, exp) + else: + return "%s*%s**%s" % (str(coeff), sbase, exp) + + return "(%s)" % " + ".join(stringify_expcoeff(i) for i in self.Data[::-1]) + + def __repr__(self): + return "%s(%s, %s)" % (self.__class__.__name, + repr(self.Base), + repr(self.Data)) + def __hash__(self): return hash(self.Base) ^ hash(self.Children) - def invoke_mapper(self, mapper): - return mapper.map_polynomial(self) + + + +if __name__ == "__main__": + xpoly = Polynomial("x", ((0,1), (2,1))) + ypoly = Polynomial("y", ((0,xpoly), (1,2), (2,-xpoly))) + print xpoly**3 + print xpoly + 1 + print xpoly**3-xpoly + print (xpoly*ypoly)**7 diff --git a/src/primitives.py b/src/primitives.py index ce3ba3b..21c31e5 100644 --- a/src/primitives.py +++ b/src/primitives.py @@ -4,7 +4,7 @@ import mapper.stringifier -class Expression: +class Expression(object): def __add__(self, other): if not isinstance(other, Expression): other = Constant(other) @@ -61,11 +61,11 @@ class Expression: other = Constant(other) if isinstance(other, Constant) and other.Value == 1: return self - return Quotient(self, other) + return RationalExpression(self, other) def __rdiv__(self, other): assert not isinstance(other, Expression) - return Quotient(Constant(other), self) + return RationalExpression(Constant(other), self) def __pow__(self, other): if not isinstance(other, Expression): @@ -228,6 +228,9 @@ class Constant(Expression): return Expression.__call__(self, *pars) return self.Value(*pars) + def __nonzero__(self): + return bool(self.Value) + def __hash__(self): return hash(self.Value) @@ -276,6 +279,15 @@ class Sum(NAryExpression): other = Constant(other) return Sum(self.Children + (-other,)) + def __nonzero__(self): + if len(self.Children) == 0: + return True + elif len(self.Children) == 1: + return bool(self.Children[0]) + else: + # FIXME: Right semantics? + return False + def invoke_mapper(self, mapper): return mapper.map_sum(self) @@ -298,12 +310,43 @@ class Product(NAryExpression): return Product(other.Children + self.Children) return Product(other, + self.Children) + def __nonzero__(self): + for i in self.Children: + if not i: + return False + return True + def invoke_mapper(self, mapper): return mapper.map_product(self) -class Quotient(BinaryExpression): +class RationalExpression(Expression): + def __init__(self, numerator=None, denominator=1, rational=None): + if rational: + self.Rational = rational + else: + self.Rational = rat.Rational(numerator, denominator) + + def _num(self): + return self.Rational.numerator + numerator=property(_num) + + def _den(self): + return self.Rational.denominator + denominator=property(_den) + + def __nonzero__(self): + return + for i in self.Children: + if not i: + return False + return True + + def __hash__(self): + return hash(self.Rational) + def invoke_mapper(self, mapper): - return mapper.map_quotient(self) + return mapper.map_rational(self) + class Power(BinaryExpression): def invoke_mapper(self, mapper): @@ -317,7 +360,7 @@ class PolynomialExpression(Expression): self.Polynomial = polynomial.Polynomial(base, data) def __hash__(self): - return ~hash(self.Polynomial) + return hash(self.Polynomial) def invoke_mapper(self, mapper): return mapper.map_polynomial(self) diff --git a/src/rational.py b/src/rational.py new file mode 100644 index 0000000..5254928 --- /dev/null +++ b/src/rational.py @@ -0,0 +1,19 @@ +class Rational(object): + def __init__(self, numerator, denominator=1): + self.Numerator = num + self.Denominator = den + + def _num(self): + return self.Numerator + numerator = property(_num) + + def _den(self): + return self.Denominator + denominator = property(_den) + + def __add__(self, other): + if not isinstance(other, Rational): + other = Rational(other) + + t = traits.most_special_traits(self.Denominator, other.Denominator) + gcd = operation.gcd(self.Denominator, other.Denominator) diff --git a/src/tests.py b/src/tests.py deleted file mode 100644 index b80e20a..0000000 --- a/src/tests.py +++ /dev/null @@ -1,11 +0,0 @@ -import primitives - - - - -def is_zero(expr): - # FIXME - return isinstance(expr, primitives.Constant) and expr.Value == 0 - -def is_one(expr): - return isinstance(expr, primitives.Constant) and expr.Value == 1 or is_zero(expr-1) diff --git a/src/traits.py b/src/traits.py new file mode 100644 index 0000000..aa6222a --- /dev/null +++ b/src/traits.py @@ -0,0 +1,42 @@ +def traits(x): + try: + return x.traits + except AttributeError: + if isinstance(x, complex): return ComplexTraits + if isinstance(x, float): return FloatTraits + if isinstance(x, int): return IntegerTraits + return NotImplementedError + + + + +def most_special_traits(x, y): + t_x = traits(x) + t_y = traits(y) + + if isinstance(t_y, t_x.__class__): + return t_y + else: + return t_x + + + + +class Traits: + def one(self): raise NotImplementedError + def zero(self): raise NotImplementedError + + def degree(self, x): + """Returns the degree of the element x. + + "Degree" is used as in the definition of a Euclidean ring, + see [Bosch], p. 42 + """" + raise NotImplementedError + + def gcd_extended(self, q, r): + """Returns a tuple + + def gcd(self): raise NotImplementedError + def lcm(self): raise NotImplementedError + def lcm_extended(self): raise NotImplementedError -- GitLab