def cos(self): """Cos function of an interval.""" if self.contained_in(Interval(expr.Const(0), expr.pi, False, False)): return Interval( expr.Fun('cos', self.end).normalize_constant(), expr.Fun('cos', self.start).normalize_constant(), self.right_open, self.left_open) elif self.contained_in( Interval(-(expr.pi / 2), expr.pi / 2, False, False)): return Interval(expr.Const(0), expr.Const(1), False, False) else: raise NotImplementedError
def eval(self, e): if isinstance(e, str): e = parser.parse_expr(e) if e.ty != expr.INTEGRAL: return e if e.body.is_constant() and e.body != Const(1): return EvalAt(e.var, e.lower, e.upper, e.body * Var(e.var)) x = Var(e.var) c = Symbol('c', [CONST]) rules = [ (Const(1), None, Var(e.var)), (c, None, c * Var(e.var)), (x, None, (x ^ 2) / 2), (x ^ c, lambda m: m[c].val != -1, lambda m: (x ^ Const(m[c].val + 1)) / (Const(m[c].val + 1))), (Const(1) / x ^ c, lambda m: m[c].val != 1, (-c) / (x ^ (c + 1))), (expr.sqrt(x), None, Fraction(2,3) * (x ^ Fraction(3,2))), (sin(x), None, -cos(x)), (cos(x), None, sin(x)), (expr.exp(x), None, expr.exp(x)), (Const(1) / x, None, expr.log(expr.Fun('abs', x))), (x ^ Const(-1), None, expr.log(expr.Fun('abs', x))), ((1 + (x ^ Const(2))) ^ Const(-1), None, expr.arctan(x)), (expr.sec(x) ^ Const(2), None, expr.tan(x)), (expr.csc(x) ^ Const(2), None, -expr.cot(x)), ] for pat, cond, pat_res in rules: mapping = expr.match(e.body, pat) if mapping is not None and (cond is None or cond(mapping)): if isinstance(pat_res, expr.Expr): integral = pat_res.inst_pat(mapping) else: integral = pat_res(mapping) return EvalAt(e.var, e.lower, e.upper, integral) return e
def __pow__(self, exp): # Assume the power is a fraction if isinstance(exp, int) or (isinstance(exp, Fraction) and exp.denominator % 2 == 1): return Monomial(self.coeff ** exp, [(n, e * exp) for n, e in self.factors]) elif isinstance(exp, Fraction) and exp.denominator % 2 == 0: sqrt_factors = [] for n, e in self.factors: if isinstance(n, expr.Expr) and isinstance(e, int) and e % 2 == 0: sqrt_factors.append((expr.Fun('abs', n), e * exp)) else: sqrt_factors.append((n, e * exp)) return Monomial(self.coeff ** exp, sqrt_factors) else: raise ValueError
def eval(self, e): rule = self.rule if e.ty in (expr.VAR, expr.CONST): return rule.eval(e) elif e.ty == expr.OP: args = [self.eval(arg) for arg in e.args] return rule.eval(expr.Op(e.op, *args)) elif e.ty == expr.FUN: args = [self.eval(arg) for arg in e.args] return rule.eval(expr.Fun(e.func_name, *args)) elif e.ty == expr.DERIV: return rule.eval(expr.Deriv(e.var, self.eval(e.body))) elif e.ty == expr.INTEGRAL: return rule.eval(expr.Integral(e.var, self.eval(e.lower), self.eval(e.upper), self.eval(e.body))) elif e.ty == expr.EVAL_AT: return rule.eval(expr.EvalAt(e.var, self.eval(e.lower), self.eval(e.upper), self.eval(e.body))) elif e.ty == expr.LIMIT: return rule.eval(expr.Limit(e.var, e.lim, self.eval(e.body))) else: raise NotImplementedError
def log(self): """Log function of an interval.""" return Interval( expr.Fun('log', self.start).normalize_constant(), expr.Fun('log', self.end).normalize_constant(), self.left_open, self.right_open)
def exp(self): """Exp function of an interval.""" return Interval( expr.Fun('exp', self.start).normalize_constant(), expr.Fun('exp', self.end).normalize_constant(), self.left_open, self.right_open)
def sqrt(self): """Square root of interval.""" return Interval( expr.Fun('sqrt', self.start).normalize_constant(), expr.Fun('sqrt', self.end).normalize_constant(), self.left_open, self.right_open)
def convert_expr(e, mode="large"): if e.ty == expr.VAR: return e.name elif e.ty == expr.CONST: if isinstance(e.val, (int, Decimal)): if e.val == Decimal("inf"): return "\\infty" elif e.val == Decimal("-inf"): return "-\\infty" else: return str(e.val) elif isinstance(e.val, Fraction): if e.val.denominator == 1: return "%d" % e.val.numerator elif mode == 'large': if e.val.numerator > 0: return "\\frac{%d}{%d}" % (e.val.numerator, e.val.denominator) elif e.val.numerator < 0: return "-\\frac{%d}{%d}" % (-e.val.numerator, e.val.denominator) else: return "%d/%d" % (e.val.numerator, e.val.denominator) else: raise NotImplementedError elif e.ty == expr.INF: return "\\infty" elif e.ty == expr.OP: if len(e.args) == 1: a, = e.args sa = convert_expr(a, mode) if a.priority() < 70: sa = "(%s)" % sa return "%s%s" % (e.op, sa) elif len(e.args) == 2: x, y = e.args sx = convert_expr(x, mode) sy = convert_expr(y, mode) if e.op in ("+", "-", "^"): if e.op == "^": # Still can improve if y.ty == expr.CONST and isinstance(y.val, Fraction): if y.val.numerator == 1: if y.val.denominator == 2: return "\\sqrt{%s}" % sx else: return "\\sqrt[%s]{%s}" % (y.val.denominator, sx) if x.ty == OP: return "(%s) ^ {%s}" % (sx, sy) return "%s ^ {%s}" % (sx, sy) if y.ty == expr.CONST and y.val < 0: if y.val != -1: new_expr = expr.Op( "/", expr.Const(1), expr.Op("^", x, expr.Const(-y.val))) else: new_expr = expr.Op("/", expr.Const(1), x) return convert_expr(new_expr) elif isinstance(x, expr.Fun) and len( x.args) > 0 and x.func_name != "abs": return "\%s^{%s}(%s)" % (x.func_name, sy, convert_expr(x.args[0])) if x.priority() < expr.op_priority[e.op]: sx = "(%s)" % sx if y.priority() < expr.op_priority[e.op]: sy = "{(%s)}" % sy if e.op == "^" and len(sy) > 1: sy = "{%s}" % sy if y.ty == expr.OP and y.op == e.op and y.op in ("-", "/"): sy = "{(%s)}" % sy return "%s %s %s" % (sx, e.op, sy) elif e.op == "*": if not x.is_constant() and not y.is_constant() and not ( y.ty == OP and y.op == "^" and y.args[1].ty == CONST and y.args[1].val < 0) or x == expr.Fun( "pi") or y == expr.Fun("pi"): if x.ty == expr.OP and (x.op not in ( "^", "*")) and not len(x.args) == 1: sx = "(" + sx + ")" if y.ty == expr.OP and y.op != "^": sy = "(" + sy + ")" return "%s %s" % (sx, sy) elif x.is_constant() and y.is_constant( ) and y.ty != CONST and not (y.ty == OP and y.op in ("+", "-") or y.ty == CONST and isinstance(y.val, Fraction)): if x == Const(-1): return "-%s" % sy if x.ty == expr.OP and x.op != "^" and len(x.args) != 1: sx = "(" + sx + ")" if y.ty == expr.OP and not ( len(y.args) == 2 and y.op == "^" or y.args[1].ty == CONST and y.args[1].val == Fraction(1 / 2)): sy = "(" + sy + ")" if x.ty == expr.CONST and isinstance( x.val, Fraction) and mode == "short": sx = "(" + sx + ")" return "%s %s" % (sx, sy) elif x.is_constant() and y.ty == CONST and isinstance( y.val, Fraction ) and y.val.numerator == 1 and y.val.denominator != 1: return "\\frac{%s}{%s}" % ( sx, convert_expr(expr.Const(y.val.denominator))) elif y.ty == OP and y.op == "^" and y.args[ 1].ty == CONST and y.args[1].val < 0: if y.args[1].val == -1: return "\\frac{%s}{%s}" % (sx, convert_expr(y.args[0])) else: new_denom = Op("^", y.args[0], Const(-y.args[1].val)) return "\\frac{%s}{%s}" % ( sx, convert_expr(new_denom, mode)) elif x.ty == expr.CONST: if x.val == -1: if y.ty == OP: return "-(%s)" % sy else: return "-%s" % sy elif x.val == 1 and not y.ty == OP: if y.ty == OP: return "(%s)" % sy else: return "%s" % sy elif isinstance( x.val, Fraction ) and x.val.numerator == 1 and y.ty not in (expr.INTEGRAL, expr.OP, expr.EVAL_AT): return "\\frac{%s}{%s}" % ( sy, convert_expr(expr.Const(x.val.denominator))) elif y.ty in (expr.VAR, expr.FUN): if isinstance(x.val, Fraction): return "(%s) %s" % (sx, sy) else: return "%s %s" % (sx, sy) elif not y.is_constant(): if y.ty == OP and y.op != '^': return "%s (%s)" % (sx, sy) elif y.ty == EVAL_AT: return "%s \\times (%s)" % (sx, sy) return "%s %s" % (sx, sy) elif y.ty != CONST and y.is_constant() and not ( y.ty == OP and y.op in ('+', '-')): return "%s %s" % (sx, sy) elif y.ty == OP and y.op == "^" and not y.args[ 0].is_constant(): return "%s %s" % (sx, sy) else: if x.priority() < expr.op_priority[e.op]: sx = "(%s)" % sx if y.priority() < expr.op_priority[e.op]: sy = "(%s)" % sy return "%s %s %s" % (sx, e.op, sy) else: if x.priority() < expr.op_priority[e.op]: sx = "(%s)" % sx if y.priority() < expr.op_priority[e.op]: sy = "(%s)" % sy return "%s %s %s" % (sx, e.op, sy) elif e.op == "/": if mode == 'large': if sy == "1": return "%s" % sx elif x.ty == expr.OP: if x.op == "-" and len(x.args) == 1: # (-x) / y => - (x/y) sxx = convert_expr(x.args[0]) return "-\\frac{%s}{%s}" % (sxx, sy) elif y.ty == expr.CONST: return "\\frac{1}{%s} \\times %s" % (sy, sx) else: return "\\frac{%s}{%s}" % (sx, sy) else: return "\\frac{%s}{%s}" % (sx, sy) else: return "%s/%s" % (sx, sy) else: raise NotImplementedError else: raise NotImplementedError elif e.ty == expr.FUN: if len(e.args) == 0: return "\\%s" % e.func_name elif len(e.args) == 1: x, = e.args sx = convert_expr(x, mode) if e.func_name == "exp": if e.args[0] == expr.Const(1): return "e" else: return "e^{%s}" % sx elif e.func_name == "sqrt": return "\\sqrt{%s}" % sx elif e.func_name == "abs": return "\\left| %s \\right|" % sx elif e.func_name == "atan": return "\\arctan{%s}" % sx elif e.func_name == "asin": return "\\arcsin{%s}" % sx elif e.func_name == "acos": return "\\arccos{%s}" % sx return "\\%s{(%s)}" % (e.func_name, sx) else: raise NotImplementedError elif e.ty == expr.INTEGRAL: lower = convert_expr(e.lower, mode='short') upper = convert_expr(e.upper, mode='short') body = convert_expr(e.body, mode) return "\\int_{%s}^{%s} %s \\,d%s" % (lower, upper, body, e.var) elif e.ty == expr.EVAL_AT: lower = convert_expr(e.lower, mode='short') upper = convert_expr(e.upper, mode='short') body = convert_expr(e.body, mode) return "\\left. %s \\right\\vert_{%s=%s}^{%s}" % (body, e.var, lower, upper) elif e.ty == expr.LIMIT: lim = convert_expr(e.lim, mode="short") body = convert_expr(e.body, mode) if e.body.ty == expr.OP and len(e.body.args) > 1: return "\\lim\\limits_{%s\\to %s} (\,%s\,)" % (e.var, lim, body) else: return "\\lim\\limits_{%s\\to %s} %s" % (e.var, lim, body) else: raise NotImplementedError
def fun_expr(self, func_name, *args): return expr.Fun(func_name, *args)