diff --git a/src/__init__.py b/src/__init__.py index 6fb21ae69a75fa569a7cae6236bbff85ad3b4ff3..af368a54830ae31602e5d17ffe3cf3eaa9947cd5 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,18 +1,19 @@ import parser -import evaluator import compiler -import stringifier -import constant_detector -import substitutor -import differentiator + +import mapper.evaluator +import mapper.stringifier +import mapper.constant_detector +import mapper.substitutor +import mapper.differentiator parse = parser.parse -evaluate = evaluator.evaluate +evaluate = mapper.evaluator.evaluate compile = compiler.compile -stringify = stringifier.stringify -is_constant = constant_detector.is_constant -substitute = substitutor.substitute -differentiate = differentiator.differentiate +stringify = mapper.stringifier.stringify +is_constant = mapper.constant_detector.is_constant +substitute = mapper.substitutor.substitute +differentiate = mapper.differentiator.differentiate if __name__ == "__main__": import math diff --git a/src/compiler.py b/src/compiler.py index cebcc1310a11d60ec6176e8a628938582a6c1fd7..0ae13d5f08d028f1794ada04e34991e9b4cba2cb 100644 --- a/src/compiler.py +++ b/src/compiler.py @@ -1,4 +1,4 @@ -import stringifier +import mapper.stringifier @@ -24,7 +24,7 @@ class CompiledExpression: used_variables.add(var) return var - pythonified = stringifier.stringify(self.Expression) + pythonified = mapper.stringifier.stringify(self.Expression) used_variables = list(used_variables) used_variables.sort() diff --git a/src/constant_detector.py b/src/constant_detector.py deleted file mode 100644 index 28224d5b859f024a801c6e4e7e1fa9ab7e5b6763..0000000000000000000000000000000000000000 --- a/src/constant_detector.py +++ /dev/null @@ -1,27 +0,0 @@ -import mapper -import operator - - - - -class ConstantDetectionMapper(mapper.CombineMapper): - def __init__(self, with_respect_to=None): - self.WRT = with_respect_to - - def combine(self, values): - return reduce(operator.and_, values) - - def map_constant(self, expr): - return True - - def map_variable(self, expr): - if self.WRT: - return expr.Name not in self.WRT - else: - return False - - - - -def is_constant(expr, with_respect_to=None): - return expr.invoke_mapper(ConstantDetectionMapper(with_respect_to)) diff --git a/src/differentiator.py b/src/differentiator.py deleted file mode 100644 index f7ec8dbb744f10280d774a9d49d30841fe9c3250..0000000000000000000000000000000000000000 --- a/src/differentiator.py +++ /dev/null @@ -1,126 +0,0 @@ -import constant_detector -import evaluator -import primitives -import mapper -import math - - - - -def map_math_functions_by_name(i, func, pars): - if not isinstance(func, primitives.Variable): - raise RuntimeError, "No derivative of non-constant function "+str(func) - name = func.Name - - if name == "sin" and len(pars) == 1: - return primitives.Constant("cos")(pars) - elif name == "cos" and len(pars) == 1: - return -primitives.Constant("sin")(pars) - elif name == "tan" and len(pars) == 1: - return primitives.Constant("tan")(pars)**2+1 - elif name == "log" and len(pars) == 1: - return primitives.Constant(1)/pars[0] - elif name == "exp" and len(pars) == 1: - return primitives.Constant("exp")(pars) - else: - return primitives.Constant(name+"'")(pars) - -class DifferentiationMapper: - def __init__(self, variable, parameters, func_map): - self.Variable = variable - self.Parameters = parameters - self.FunctionMap = func_map - - def map_constant(self, expr): - return primitives.Constant(0) - - def map_variable(self, expr): - if expr.Name is self.Variable: - return primitives.Constant(1) - elif expr.Name in self.Parameters: - return expr - else: - return primitives.Constant(0) - - def map_call(self, expr): - return primitives.make_sum(tuple( - self.FunctionMap(i, expr.Function, expr.Parameters) - * par.invoke_mapper(self) - for i, par in enumerate(expr.Parameters) - if not self._isc(par))) - - def map_sum(self, expr): - return primitives.make_sum(tuple(child.invoke_mapper(self) - for child in expr.Children - if not self._isc(child))) - - def map_neg(self, expr): - return -expr.Child.invoke_mapper(self) - - def map_product(self, expr): - return primitives.make_sum(tuple( - primitives.make_product(expr.Children[0:i] + - (child.invoke_mapper(self),) + - expr.Children[i+1:]) - for i, child in enumerate(expr.Children) - if not self._isc(child))) - - def map_quotient(self, expr): - f = expr.Child1 - g = expr.Child2 - f_const = self._isc(f) - g_const = self._isc(g) - - if f_const and g_const: - return primitives.Constant(0) - elif f_const: - f = self._eval(f) - return -f*g.invoke_mapper(self)/g**2 - elif g_const: - g = self._eval(g) - return f.invoke_mapper(self)/g - else: - return (f.invoke_mapper(self)*g-g.invoke_mapper(self)*f)/g**2 - - def map_power(self, expr): - f = expr.Child1 - g = expr.Child2 - f_const = self._isc(f) - g_const = self._isc(g) - - log = primitives.Constant("log") - - if f_const and g_const: - return primitives.Constant(0) - elif f_const: - f = self._eval(f) - return log(f) * f**g * g.invoke_mapper(self) - elif g_const: - g = self._eval(g) - return g * f**(g-1) * f.invoke_mapper(self) - else: - return log(f) * f**g * g.invoke_mapper(self) + \ - g * f**(g-1) * f.invoke_mapper(self) - - def map_polynomial(self, expr): - raise NotImplementedError - - def _isc(self,subexp): - return constant_detector.is_constant(subexp, [self.Variable]) - - def _eval(self,subexp): - try: - return primitives.Constant(evaluator.evaluate(subexp)) - except KeyError: - return subexp - - - - -def differentiate(expression, - variable, - parameters=[], - func_mapper=map_math_functions_by_name): - return expression.invoke_mapper(DifferentiationMapper(variable, - parameters, - func_mapper)) diff --git a/src/evaluator.py b/src/evaluator.py deleted file mode 100644 index 524f66d8087a58ed79edb958035a34df8c431205..0000000000000000000000000000000000000000 --- a/src/evaluator.py +++ /dev/null @@ -1,54 +0,0 @@ -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] - - - - -def evaluate(expression, context={}): - return expression.invoke_mapper(EvaluationMapper(context)) - diff --git a/src/mapper.py b/src/mapper.py deleted file mode 100644 index d126fa049e4f79cdec28d8a7000c3458e59057c7..0000000000000000000000000000000000000000 --- a/src/mapper.py +++ /dev/null @@ -1,129 +0,0 @@ -class ByArityMapper: - def map_sum(self, expr): - return self.map_n_ary(expr) - - def map_product(self, expr): - return self.map_n_ary(expr) - - 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) - - def map_list(self, expr): - return self.map_n_ary(expr) - - - - -class CombineMapper(ByArityMapper): - def combine(self, values): - raise NotImplementedError - - def map_unary(self, expr): - return expr.Child.invoke_mapper(self) - - def map_binary(self, expr): - return self.combine((expr.Child1.invoke_mapper(self), - expr.Child2.invoke_mapper(self))) - - def map_n_ary(self, expr): - return self.combine(child.invoke_mapper(self) - for child in expr.Children) - - def map_polynomial(self, expr): - return self.combine((expr.Base.invoke_mapper(self)) - + (child.invoke_mapper(self) - for child in expr.Children)) - - def map_call(self, expr): - return self.combine((expr.Function.invoke_mapper(self)) - + (child.invoke_mapper(self) - for child in expr.Parameters)) - - - - -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)) - - def map_binary(self, expr): - return expr.__class__(expr.Child1.invoke_mapper(self), - expr.Child2.invoke_mapper(self)) - - def map_n_ary(self, expr): - return expr.__class__(tuple(child.invoke_mapper(self) - for child in expr.Children)) - - def map_constant(self, expr): - return expr - - def map_variable(self, expr): - return expr - - def map_polynomial(self, expr): - return expr.__class__(expr.Base.invoke_mapper(self), - tuple(child.invoke_mapper(self) - for child in expr.Children)) - - def map_call(self, expr): - return expr.__class__(expr.Function.invoke_mapper(self), - tuple(child.invoke_mapper(self) - for child in expr.Parameters)) diff --git a/src/primitives.py b/src/primitives.py index 15d025f0ea38833fcdcf65ed51726a6ae53034c7..ce3ba3b97d2becb9cdf0a2afebfb06b2a60c0a9a 100644 --- a/src/primitives.py +++ b/src/primitives.py @@ -1,5 +1,5 @@ import tests -import stringifier +import mapper.stringifier @@ -40,7 +40,7 @@ class Expression: def __mul__(self, other): if not isinstance(other, Expression): other = Constant(other) - if test.is_one(other): + if tests.is_one(other): return self if tests.is_zero(other): return Constant(0) @@ -87,18 +87,18 @@ class Expression: def __neg__(self): return Negation(self) - def __call__(self, other): + def __call__(self, *pars): processed = [] - for par in other: + for par in pars: if isinstance(par, Expression): processed.append(par) else: processed.append(Constant(par)) - return Call(self, processed) + return Call(self, tuple(processed)) def __str__(self): - return self.invoke_mapper(stringifier.StringifyMapper()) + return self.invoke_mapper(mapper.stringifier.StringifyMapper()) class UnaryExpression(Expression): def __init__(self, child): @@ -309,66 +309,15 @@ class Power(BinaryExpression): def invoke_mapper(self, mapper): return mapper.map_power(self) -class Polynomial(Expression): - def __init__(self, base, coeff): - self.Base = base - - # list of (exponent, coefficient tuples) - # sorted in increasing order - # one entry per degree - self.Data = children - - # Remember the Zen, Luke: Sparse is better than dense. - - def __neg__(self): - return Polynomial(self.Base, - [(exp, -coeff) - for (exp, coeff) in self.Data]) - - def __add__(self, other): - if not isinstance(other, Polynomial) or other.Base != self.Base: - return Expression.__add__(self, other) - - iself = 0 - iother = 0 - - result = [] - while iself < len(self.Data) and iother < len(other.Data): - exp_self = self.Data[iself][0] - exp_other = other.Data[iother][0] - if exp_self == exp_other: - coeff = self.Data[iself][1] + other.Data[iother][1] - if coeff != Constant(0): - result.append((exp_self, coeff)) - iself += 1 - iother += 1 - elif exp_self > exp_other: - result.append((exp_other, other.Data[iother][1])) - iother += 1 - elif exp_self < exp_other: - result.append((exp_self, self.Data[iself][1])) - iself += 1 - - # we have exhausted at least one list, exhaust the other - while iself < len(self.Data): - exp_self = self.Data[iself][0] - result.append((exp_self, self.Data[iself][1])) - iself += 1 - - while iother < len(other.Data): - exp_other = other.Data[iother][0] - result.append((exp_other, other.Data[iother][1])) - iother += 1 - - return Polynomial(self.Base, result) - - def __mul__(self, other): - if not isinstance(other, Polynomial) or other.Base != self.Base: - return Expression.__mul__(self, other) - raise NotImplementedError +class PolynomialExpression(Expression): + def __init__(self, base=None, data=None, polynomial=None): + if polynomial: + self.Polynomial = polynomial + else: + self.Polynomial = polynomial.Polynomial(base, data) def __hash__(self): - return hash(self.Base) ^ hash(self.Children) + return ~hash(self.Polynomial) def invoke_mapper(self, mapper): return mapper.map_polynomial(self) diff --git a/src/stringifier.py b/src/stringifier.py deleted file mode 100644 index 71c9283ecad572f897c6e1b0e5ae48fb755193a7..0000000000000000000000000000000000000000 --- a/src/stringifier.py +++ /dev/null @@ -1,40 +0,0 @@ -class StringifyMapper: - def map_constant(self, expr): - return str(expr.Value) - - def map_variable(self, expr): - return expr.Name - - def map_call(self, expr): - return "%s(%s)" % \ - (expr.Function.invoke_mapper(self), - ", ".join(i.invoke_mapper(self) for i in expr.Parameters)) - - def map_sum(self, expr): - return "(%s)" % "+".join(i.invoke_mapper(self) for i in expr.Children) - - def map_negation(self, expr): - return "-%s" % expr.Child.invoke_mapper(self) - - def map_product(self, expr): - return "(%s)" % "*".join(i.invoke_mapper(self) for i in expr.Children) - - def map_quotient(self, expr): - return "(%s/%s)" % (expr.Child1.invoke_mapper(self), - expr.Child2.invoke_mapper(self)) - - def map_power(self, expr): - return "(%s**%s)" % (expr.Child1.invoke_mapper(self), - expr.Child2.invoke_mapper(self)) - - def map_polynomial(self, expr): - raise NotImplementedError - - def map_list(self, expr): - return "[%s]" % ", ".join([i.invoke_mapper(self) for i in expr.Children]) - - - - -def stringify(expression): - expression.invoke_mapper(StringifyMapper) diff --git a/src/substitutor.py b/src/substitutor.py deleted file mode 100644 index c49b5ccb7466d37ca88d720d04338ab6a7ef53e9..0000000000000000000000000000000000000000 --- a/src/substitutor.py +++ /dev/null @@ -1,20 +0,0 @@ -import mapper - - - - -class SubstitutionMapper(mapper.IdentityMapper): - def __init__(self, variable_assignments): - self.Assignments = variable_assignments - - def map_variable(self, expr): - try: - return self.Assignments[expr.Name] - except KeyError: - return expr - - - - -def substitute(expression, variable_assignments = {}): - return expression.invoke_mapper(SubstitutionMapper(variable_assignments))