def test_unitize(): assert p("Unitize[Pi]") == 1 assert p("Unitize[Sqrt[2] + Sqrt[3] - Sqrt[5 + 2 Sqrt[6]]]") == 0 assert p( "m = {{-2, 1, 0, 1}, {1, -2, 1, 0}, {0, 1, -2, 1}, {1, 0, -2, 1}}; Unitize[m]" ) == List(List(1, 1, 0, 1), List(1, 1, 1, 0), List(0, 1, 1, 1), List(1, 0, 1, 1))
def test_rescale(): assert p_str("Rescale[2.5, {-10, 10}]").startswith("0.6250") assert p_str("Rescale[12.5, {-10, 10}]").startswith("1.1250") assert p("Rescale[-3/2, {-2, 2}]") == f(1, 8) assert p_str("N[Rescale[Pi, {0, 2.5}]]").startswith("1.2566") assert p("Rescale[3, {-9, 7}, {11, 28}]") == f(95, 4) assert p("Expand[Rescale[1 + 2 I, {0, 5}]]") == f(1, 5) + 2 * s.I / 5 assert p("Expand[Rescale[1 + 2 I, {0, 1 + I}]]") == f(3, 2) + s.I / 2 assert p("Rescale[{-2, 0, 2}]") == List(0, f(1, 2), 1) assert p("Rescale[{-2, 0, 2}, {-5, 5}]") == List(f(3, 10), f(1, 2), f(7, 10)) assert p("Rescale[{-2, 0, 2}, {-5, 5}, {-1, 1}]") == List( f(-2, 5), 0, f(2, 5))
def thread(func, *args, **kwargs): """ Internal threading function keyword args are not threaded """ length = None for arg in args: if isinstance(arg, iterables): if length is not None: if length != len(arg): raise FunctionException( "General::thread", "Cannot Thread over Lists of Unequal Length.", ) else: length = len(arg) if length is None: return func(*args, **kwargs) chained = list(args) for i in range(len(chained)): if not isinstance(chained[i], iterables): chained[i] = (chained[i],) * length return List.create(thread(func, *z, **kwargs) for z in zip(*chained))
def test_ramp(): assert p("Ramp[-1]") == 0 assert p_str("Ramp[3.7]").startswith("3.70") assert p("Ramp[1/2]") == f(1, 2) assert p("Ramp[-E]") == 0 assert p("Ramp[-1.55]") == 0 assert p("Ramp[{-5, -1.5, 0, 1/3, Pi}]") == List(0, 0, 0, f(1, 3), s.pi)
def test_sign(): assert p("Sign[-2.5]") == -1 assert p("Sign[3.14]") == 1 assert p("Sign[1 + I]") == (1 + s.I) / s.sqrt(2) assert p("Sign[{1.2, 1.5, -1.8}]") == List(1, 1, -1) assert p("Sign[0]") == 0 assert p("Sign[Infinity]") == 1 assert p("Sign[-Infinity]") == -1
def test_clip(): assert p("Clip[8.5]") == 1 assert p("Clip[-5/2, {-2, 2}]") == -2 assert p("Clip[Pi, {-9, 7}, {11, 28}]") == s.pi assert p("Clip[3 Pi, {-9, 7}, {11, 28}]") == 28 assert p("Clip[{-2, 0, 2}]") == List(-1, 0, 1) assert p("Clip[0]") == 0 assert p("Clip[Infinity]") == 1
def test_int_part(): assert p("IntegerPart[2.4]") == 2 assert p("IntegerPart[-2.4]") == -2 assert p("IntegerPart[456.457]") == 456 assert p("IntegerPart[-5/4]") == -1 assert p("IntegerPart[Pi + E]") == 5 assert p("IntegerPart[78/47 - 4.2 I]") == 1 - 4 * s.I assert p("IntegerPart[{2.4, 2.5, 3.0}]") == List(2, 2, 3) assert p("IntegerPart[0]") == 0 assert p("IntegerPart[Infinity]") == s.oo
def test_round(): assert p("Round[2.4]") == 2 assert p("Round[2.6]") == 3 assert p("Round[226, 10]") == 230 assert p("Round[-3.7]") == -4 assert p_str("Round[-10.3, 3.5]").startswith("-10.50") assert p("Round[2 Pi - E, 5/4]") == f("15/4") assert p("Round[5.37 - 1.3 I]") == 5 - s.I assert p("Round[{2.4, 2.5, 2.6}]") == List(2, 2, 3) assert p("Round[0]") == 0 assert p("Round[Infinity]") == s.oo
def r_thread(func, to_thread, *args, rule=True, **kwargs): if isinstance(to_thread, iterables): return List(*(r_thread(func, x, *args, **kwargs) for x in to_thread)) if rule and isinstance(to_thread, Rule): return Rule( r_thread(func, to_thread.lhs, *args, **kwargs), r_thread(func, to_thread.rhs, *args, **kwargs), ) return func(to_thread, *args, **kwargs)
def evaluate_no_lazy(expr): """ Evaluate an expression with current definitions. Does not evaluate any LazyFunctions in given expression. """ # if is iterable, apply for each element if isinstance(expr, iterables) and not isinstance(expr, s.Matrix): return List.create(evaluate_no_lazy(x) for x in expr) # if expr does not have ability to perform substitutions, return if not hasattr(expr, "xreplace"): return expr def extend(ex): # if ex is a number, return if hasattr(ex, "is_Number") and ex.is_Number: return ex # if ex is a symbol, return definitions if present if isinstance(ex, s.Symbol): st = ex.name if st in r.refs.OwnValues: return r.refs.OwnValues[st] elif st in r.refs.Constants.Dict: return r.refs.Constants.Dict[st] # if ex does not have any arguments, return if not hasattr(ex, "args") or not isinstance(ex.args, iterables): return ex # ex is supposed to be a function at this point (since args are present) f_name = type(ex).__name__ # do not make substitutions if function is explicit # since explicit functions require unevaluated args if Functions.is_explicit(f_name): return ex # evaluate args rep = {x: extend(x) for x in ex.args} ex = ex.xreplace(rep) # built-ins are automatically re-evaluated # when replacements are made by sympy # apply definitions if function has any if Functions.is_not_builtin(f_name): ex = Functions.apply_definitions(ex) return ex # evaluate inside-out return extend(expr)
def test_rule(): assert p("1 -> 2") == Rule(1, 2) assert p("2 -> 3").lhs == 2 r = p("1 -> 2") r.lhs += 1 assert p("2 -> 2") == r assert (p("1 -> 2") == r) is False assert (r.lhs, r.rhs) == (r[0], r[1]) assert list(r) == [2, 2] assert Rule(1, 2).__repr__() == Rule(1, 2).__str__() == "1 -> 2" assert Rule.from_dict({1: 2, 3: 4}, head=List) == List(Rule(1, 2), Rule(3, 4))
def test_floor(): assert p("Floor[2.4]") == 2 assert p("Floor[2.6]") == 2 assert p("Floor[226, 10]") == 220 assert p("Floor[5.37]") == 5 assert p("Floor[-3.7]") == -4 assert p_str("Floor[-10.3, 3.5]").startswith("-10.50") assert p("Floor[2 Pi - E, 5/4]") == f("5/2") assert p("Floor[5.37 - 1.3 I]") == 5 - 2 * s.I assert p("Floor[{2.4, 2.5, 2.6}]") == List(2, 2, 2) assert p("Floor[0]") == 0 assert p("Floor[Infinity]") == s.oo assert p("Simplify[Floor[x + 1]]") == p("1 + Floor[x]")
def test_ceiling(): assert p("Ceiling[2.4]") == 3 assert p("Ceiling[2.6]") == 3 assert p("Ceiling[226, 10]") == 230 assert p("Ceiling[5.37]") == 6 assert p("Ceiling[-3.7]") == -3 assert p_str("Ceiling[-10.3, 3.5]").startswith("-7.0") assert p("Ceiling[2 Pi - E, 5/4]") == f("15/4") assert p("Ceiling[5.37 - 1.3 I]") == 6 - s.I assert p("Ceiling[{2.4, 2.5, 2.6}]") == List(3, 3, 3) assert p("Ceiling[0]") == 0 assert p("Ceiling[Infinity]") == s.oo assert p("Simplify[Ceiling[x + 1]]") == p("1 + Ceiling[x]")
def operate(tree: Tree): if not isinstance(tree, Tree): return tree if tree.data == "symbol": return symbol(tree.children[0]) if tree.data in basic_ops: return basic(tree.data, [operate(x) for x in tree.children], lazy_call=False) if tree.data == "function": name = str(tree.children[0].children[0]) if Functions.is_explicit(name): return Functions.call(name, *(lazy(x) for x in tree.children[1:])) return Functions.call(name, *(operate(x) for x in tree.children[1:])) if tree.data == "list": return List(*(operate(x) for x in tree.children)) if tree.data == "rule": return Rule(*(operate(x) for x in tree.children)) if tree.data == "part": return Functions.call("Part", *(operate(x) for x in tree.children)) if tree.data == "out": return out(tree.children) if tree.data == "set": return Functions.call("Set", lazy(tree.children[0]), operate(tree.children[1])) if tree.data == "set_delayed": return Functions.call("SetDelayed", lazy(tree.children[0]), lazy(tree.children[1])) if tree.data == "replace": # see ReplaceAll return Functions.call("Replace", *(operate(x) for x in tree.children)) if tree.data == "unset": return Functions.call("Unset", lazy(tree.children[0])) if tree.data == "semicolon_statement": return Functions.call("SemicolonStatement", *(lazy(x) for x in tree.children)) if tree.data == "compound_statement": return Functions.call("CompoundExpression", *(lazy(x) for x in tree.children)) if tree.data == "postfix": name = str(tree.children[-1].children[0]) if Functions.is_explicit(name): return Functions.call(name, *(lazy(x) for x in tree.children[:-1])) return Functions.call(name, *(operate(x) for x in tree.children[:-1])) if tree.data == "RELATIONAL": return str(tree.children[0]) if tree.data == "relation": return relations([operate(x) for x in tree.children]) if tree.data == "tag": return tag(*(operate(x) for x in tree.children)) if tree.data == "span": return spanner(tree.children, operate) return tree
def lazy(tree: Tree): if not isinstance(tree, Tree): return tree if tree.data == "symbol": return s.Symbol(tree.children[0]) if tree.data in basic_ops: return basic(tree.data, [lazy(x) for x in tree.children], lazy_call=True) if tree.data == "function": return Functions.lazy_call( str(tree.children[0].children[0]), *(lazy(x) for x in tree.children[1:]), ) if tree.data == "list": return List(*(lazy(x) for x in tree.children)) if tree.data == "rule": return Rule(*(lazy(x) for x in tree.children)) if tree.data == "part": return Functions.lazy_call("Part", *(lazy(x) for x in tree.children)) if tree.data == "out": return out(tree.children) if tree.data == "set": return Functions.lazy_call("Set", lazy(tree.children[0]), lazy(tree.children[1])) if tree.data == "set_delayed": return Functions.lazy_call("SetDelayed", lazy(tree.children[0]), lazy(tree.children[1])) if tree.data == "replace": return Functions.lazy_call("Replace", *(lazy(x) for x in tree.children)) if tree.data == "semicolon_statement": return Functions.lazy_call("SemicolonStatement", *(lazy(x) for x in tree.children)) if tree.data == "compound_statement": return Functions.lazy_call("CompoundExpression", *(lazy(x) for x in tree.children)) if tree.data == "postfix": return Functions.lazy_call( str(tree.children[-1].children[0]), *(lazy(x) for x in tree.children[:-1]), ) if tree.data == "RELATIONAL": return str(tree.children[0]) if tree.data == "relation": return relations([lazy(x) for x in tree.children]) if tree.data == "tag": return lazy_tag(*(lazy(x) for x in tree.children)) if tree.data == "span": return spanner(tree.children, lazy)
def evaluate(expr): """ Evaluate an expression with current definitions. Evaluates all LazyFunctions in given expression. """ # see evaluate_no_lazy for explanation if isinstance(expr, iterables) and not isinstance(expr, s.Matrix): return List.create(LazyFunction.evaluate(x) for x in expr) if not hasattr(expr, "subs"): return expr def extend(ex): if hasattr(ex, "is_Number") and ex.is_Number: return ex if isinstance(ex, s.Symbol): st = ex.name if st in r.refs.OwnValues: return r.refs.OwnValues[st] elif st in r.refs.Constants.Dict: return r.refs.Constants.Dict[st] if not hasattr(ex, "args") or not isinstance(ex.args, iterables): return ex f_name = type(ex).__name__ if Functions.is_explicit(f_name): # if explicit lazy function, call with args un-evaluated if isinstance(ex, LazyFunction): return ex.land() return ex rep = {x: extend(x) for x in ex.args} ex = ex.xreplace(rep) if Functions.is_not_builtin(f_name): ex = Functions.apply_definitions(ex) # if lazy function, call actual function if isinstance(ex, LazyFunction): return ex.land() return ex return extend(expr)
def exec(cls, expr, replacements): if not isinstance(replacements, iterables): replacements = (replacements,) else: if isinstance(replacements[0], iterables): if not all(isinstance(x, iterables) for x in replacements): raise FunctionException( "Replace::subs", f"{replacements} is a mixture of Lists and Non-Lists.", ) return List( *(cls.exec(expr, replacement) for replacement in replacements) ) else: if not all(not isinstance(x, iterables) for x in replacements): raise FunctionException( "Replace::subs", f"{replacements} is a mixture of Lists and Non-Lists.", ) if isinstance(expr, iterables) and not isinstance(expr, s.Matrix): return List(*(cls.do_subs(x, replacements) for x in expr)) return cls.do_subs(expr, replacements)
def __init__(self): self.In = [] self.Out = [] self.Messages = [List()] self.OwnValues = OwnValues() self.NoCache = NoCache self.BuiltIns = DownValues() self.DownValues = DownValues() self.TagValues = TagValues() self.FunctionWrappers = FunctionWrappers self.Constants = Constants self.Protected = Protected self.Line = 1 self.CacheClearQueued = False # dirty, but works self.Parser = None self.Messenger = None self.DefaultPrecision = 9 self.ExtraPrecision = 5 self.WorkingPrecision = self.DefaultPrecision + self.ExtraPrecision
def add_def(self, _in, out): self.In.append(_in) self.Out.append(out) self.Messages.append(List()) self.Line += 1
def test_part(): a, b, c, d, e, f, g, h, i = s.var("a b c d e f g h i") assert p("Part[{a, b, c, d, e}, 3]") == c assert p("{a, b, c, d, e, f}[[3]]") == c assert p("{{a, b, c}, {d, e, f}}[[1]][[2]]") == b assert p("{{a, b, c}, {d, e, f}}[[1, 2]]") == b assert p("{{a, b, c}, {d, e, f}, {g, h, i}}[[{1, 3}]]") == List( List(a, b, c), List(g, h, i)) assert p("{{a, b, c}, {d, e, f}, {g, h, i}}[[{1, 3}, {2, 3}]]") == List( List(b, c), List(h, i)) assert p("{{a, b, c}, {d, e, f}, {g, h, i}}[[2, 3]]") == f assert p("{{a, b, c}, {d, e, f}, {g, h, i}}[[2]]") == List(d, e, f) assert p("{{a, b, c}, {d, e, f}, {g, h, i}}[[All, 2]]") == List(b, e, h) assert p("{a, b, c, d, e, f}[[-2]]") == e assert p("{a, b, c, d, e, f}[[{1, 3, 1, 2, -1, -1}]]") == List( a, c, a, b, f, f) assert p("{a, b, c, d, e, f}[[2 ;; 4]]") == List(b, c, d) assert (p("{a, b, c, d, e, f}[[1 ;; -3]]") == p("{a, b, c, d, e, f}[[;; -3]]") == List(a, b, c, d)) assert p("{a, b, c, d, e, f, g, h, i, j}[[3 ;; -3 ;; 2]]") == List(c, e, g) assert p("{a, b, c, d, e, f, g, h, i, j}[[;; ;; 2]]") == List( a, c, e, g, i) assert p("{{a, b}, {c, d}, {e, f}}[[1;;2]][[2]]") == List(c, d) assert p("{{a, b}, {c, d}, {e, f}}[[1;;2, 2]]") == List(b, d) assert p("f[g[a, b], g[c, d]][[2, 1]]") == c assert p("(1 + 2 a^2 + b^2)[[2]]") == b**2 assert p("{a -> c, b -> d}[[1, 2]]") == c assert p("(a/b)[[2]]") == 1 / b assert p("{a, b, c}[[0]]") == p("List") assert p("f[a, b, c][[{2, 3}]] == f[b, c]") assert p("f[g[a, b], h[c, d]][[{1, 2}, {2}]] == f[g[b], h[d]]")
def test_take(): a, b, c, d, e, f, t, u = s.var("a b c d e f t u") assert p("Take[{a, b, c, d, e, f}, 4]") == List(a, b, c, d) assert p("Take[{a, b, c, d, e, f}, -3]") == List(d, e, f) assert p("Take[{a, b, c, d, e, f}, {2, -2}]") == List(b, c, d, e) assert p("Take[{a, b, c, d, e, f}, {1, -1, 2}]") == List(a, c, e) assert p("Take[{a, b, c, d, e}, -1]") == List(e) assert p("Take[{{11, 12, 13}, {21, 22, 23}, {31, 32, 33}}, 2]") == List( List(11, 12, 13), List(21, 22, 23)) assert p( "Take[{{11, 12, 13}, {21, 22, 23}, {31, 32, 33}}, All, 2]") == List( List(11, 12), List(21, 22), List(31, 32)) assert p( "Take[{{11, 12, 13}, {21, 22, 23}, {31, 32, 33}}, 2, -1]") == List( List(13), List(23)) m = "{{11,12,13,14,15},{21,22,23,24,25},{31,32,33,34,35},{41,42,43,44,45},{51,52,53,54,55}}" assert p("Take[" + m + ", {2, 4}, {3, 5}]") == List( List(23, 24, 25), List(33, 34, 35), List(43, 44, 45)) assert p("Take[" + m + ", {1, -1, 2}, {1, -1, 2}]") == p( "{{11,13,15},{31,33,35},{51,53,55}}") assert p("Take[a + b + c + d + e + f, 3]") == a + b + c assert p("Take[{a + b + c, t + u + v, x + y + z}, 2, 2]") == List( a + b, t + u)
def do_set(x, n): """Handles assignment""" refs = r.refs if isinstance(x, s.Symbol): if not is_assignable(x.name): raise FunctionException( "Set::setx", f"Symbol {x} is protected and cannot be assigned to.", ) if isinstance(x, Tag): set_tag_value(x, n) return n if isinstance(n, s.Expr): if x in n.atoms(): return None # TODO: raise refs.OwnValues[x.name] = n return n if isinstance(x, s.Function): name = type(x).__name__ # handle part assignment if name == "Part": return set_part(x, n) if not is_assignable(name): raise FunctionException("Set::set", f"Symbol {x} cannot be Assigned to.") # create dict of patterns if not present if name not in refs.DownValues: refs.DownValues[name] = {ArgsPattern(*x.args): n} # else add pattern to existing dict else: pat = ArgsPattern(*x.args) # remove duplicate if present remove_duplicate_pattern(refs.DownValues[name], pat) # update in place refs.DownValues[name].update({pat: n}) # sort dict when done # sorting is done during assignment to keep function calls fast refs.DownValues[name] = OrderedDict( sorted( refs.DownValues[name].items(), key=lambda z: z[0].importance, reverse=True, ) ) return n if isinstance(x, iterables): if isinstance(n, iterables) and len(x) == len(n): return List.create(Set(a, b) for a, b in zip(x, n)) else: raise FunctionException("Set::shape") return None
def exec(cls, n): if isinstance(n, iterables): return List.create(Unset(x) for x in n) if isinstance(n, s.Symbol) and str(n) in r.refs.OwnValues: del r.refs.OwnValues[str(n)] return NoOutput(None)
def test_list(): assert p("{}") == List() assert p("{1, 2, 3}") == List(1, 2, 3) d = p("{1, 2, 3, 4, 5}") assert d[0] == 1 assert d[:2] == List(1, 2) d[0] = 2 assert d[0] == 2 d = d.concat(List(6)) assert d == List(2, 2, 3).concat(List(4, 5, 6)) assert p("{1, 2} + {3, 4}") == List(4, 6) assert p("{1, 2} - {3, 4}") == List(-2, -2) assert p("{1, 2} * 2") == List(2, 4) assert p("2 * {1, 2}") == List(2, 4) assert p("{1, 2} * {3, 4}") == List(3, 8) assert p("{1, 2} / 2") == List(f(1, 2), 1) assert p("2 / {1, 2}") == List(2, 1) assert p("{1, 2} / {4, 3}") == List(f(1, 4), f(2, 3)) assert p("1 + {1, 2, 3, 4}") == List(2, 3, 4, 5) assert p("{1, 2, 3, 4} + 1") == List(2, 3, 4, 5) assert p("1 - {1, 2, 3, 4}") == List(0, -1, -2, -3) assert p("{1, 2, 3, 4} - 1") == List(0, 1, 2, 3) assert p("{1, 2, 3}").evalf() == List(1, 2, 3) assert p("N[{1, 2, 3}]") == List(1, 2, 3) assert p("2 ^ {1, 2, 3}") == List(2, 4, 8) assert p("{1, 2, 3} ^ 2") == List(1, 4, 9) assert p("{1, 2, 3} ^ {3, 2, 1}") == List(1, 4, 3) assert p("{1, 2, 3, x}") == p("{2 - 1, 2, 2 + 1, x}") assert p("{1, 2}").__repr__() == p("{1, 2}").__str__() == "{1, 2}" assert list(p("{1, 2, 3}")) == [1, 2, 3]