Things to improve: * pattern matching * subexpression substitution * unifying and especially simplify the way to check the type of an expression. Now you need to do this ugly switch: def _is(e, what): import operator if what == "Mul": return isinstance(e, sage.calculus.calculus.SymbolicArithmetic) and \ e._operator == operator.mul if what == "Add": return isinstance(e, sage.calculus.calculus.SymbolicArithmetic) and \ e._operator == operator.add if what == "Pow": return isinstance(e, sage.calculus.calculus.SymbolicArithmetic) and \ e._operator == operator.pow if what == "Div": return isinstance(e, sage.calculus.calculus.SymbolicArithmetic) and \ e._operator == operator.div if what == "log": return isinstance(e, sage.calculus.calculus.SymbolicComposition) and \ bool(e._operands[0] == sage.all.log) if what == "exp": return isinstance(e, sage.calculus.calculus.SymbolicComposition) and \ bool(e._operands[0] == sage.all.exp) if what == "Function": return isinstance(e, sage.calculus.calculus.SymbolicComposition) elif what == "Rational": return isinstance(e, sage.rings.rational.Rational) elif what == "Real": return isinstance(e, sage.rings.real_mpfr.RealNumber) else: raise "Sorry, unknown 'class': %s" % what Those are just things I discovered when trying to port the limits from SymPy to SAGE. Then there are other things, for example: * working with unknown functions, expanding them in series, etc. (there is some trac ticket for that already)