Skip to content

Commit

Permalink
Merge pull request #159 from sympy/upgrade-sympy-1
Browse files Browse the repository at this point in the history
Upgrade SymPy to 1.6
  • Loading branch information
aktech committed Jun 25, 2020
2 parents 3975dd5 + 6d7e1ca commit 20131c6
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 29 deletions.
4 changes: 2 additions & 2 deletions app/logic/intsteps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sympy.integrals.manualintegrate import (
_manualintegrate, integral_steps, evaluates,
ConstantRule, ConstantTimesRule, PowerRule, AddRule, URule,
PartsRule, CyclicPartsRule, TrigRule, ExpRule, LogRule, ArctanRule,
PartsRule, CyclicPartsRule, TrigRule, ExpRule, ReciprocalRule, ArctanRule,
AlternativeRule, DontKnowRule, RewriteRule
)

Expand Down Expand Up @@ -71,7 +71,7 @@ def print_rule(self, rule):
self.print_Trig(rule)
elif isinstance(rule, ExpRule):
self.print_Exp(rule)
elif isinstance(rule, LogRule):
elif isinstance(rule, ReciprocalRule):
self.print_Log(rule)
elif isinstance(rule, ArctanRule):
self.print_Arctan(rule)
Expand Down
5 changes: 1 addition & 4 deletions app/logic/logic.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
from __future__ import absolute_import
import sys
import traceback
import collections
from .utils import Eval, latexify, arguments, removeSymPy, \
custom_implicit_transformation, synonyms, OTHER_SYMPY_FUNCTIONS, \
close_matches
from .resultsets import find_result_set, get_card, format_by_type, \
is_function_handled, find_learn_more_set
from sympy import latex, series, sympify, solve, Derivative, \
Integral, Symbol, diff, integrate
from sympy import latex
import sympy
from sympy.core.function import FunctionClass
from sympy.parsing.sympy_parser import stringify_expr, eval_expr, \
Expand Down
3 changes: 2 additions & 1 deletion app/logic/resultsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ def is_approximatable_constant(input_evaluated):
# is_constant, but exclude Integer/Float/infinity
return (hasattr(input_evaluated, 'free_symbols') and
not input_evaluated.free_symbols and
not input_evaluated.is_Integer and
hasattr(input_evaluated, 'is_Integer')
and input_evaluated.is_Integer and
not input_evaluated.is_Float and
input_evaluated.is_finite is not True)

Expand Down
44 changes: 37 additions & 7 deletions app/logic/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@
import sympy

from sympy.core.relational import Relational
import sympy.parsing.sympy_tokenize as sympy_tokenize
from token import NAME
import tokenize as sympy_tokenize
from six.moves import map
from six.moves import zip

from sympy.parsing.sympy_parser import (
AppliedFunction, implicit_multiplication, split_symbols,
function_exponentiation, implicit_application, OP, NAME,
_group_parentheses, _apply_functions, _flatten, _token_callable
)

OTHER_SYMPY_FUNCTIONS = ('sqrt',)

Arguments = collections.namedtuple('Arguments', 'function args kwargs')


class Eval(object):
def __init__(self, namespace={}):
self._namespace = namespace
Expand Down Expand Up @@ -79,6 +85,7 @@ def eval(self, x, use_none_for_exceptions=False, repr_expression=True):
s = "".join(traceback.format_exception(etype, value, tb))
return s


class LatexVisitor(ast.NodeVisitor):
EXCEPTIONS = {'integrate': sympy.Integral, 'diff': sympy.Derivative}
formatters = {}
Expand Down Expand Up @@ -129,6 +136,7 @@ def visit_Call(self, node):
self.latex = sympy.latex(self.evaluator.eval_node(node))
return self.latex


@LatexVisitor.formats_function('solve')
def format_solve(node, visitor):
expr = visitor.evaluator.eval_node(node.args[0])
Expand All @@ -147,12 +155,14 @@ def format_solve(node, visitor):

return ''.join(buffer)


@LatexVisitor.formats_function('limit')
def format_limit(node, visitor):
if len(node.args) >= 3:
return sympy.latex(
sympy.Limit(*[visitor.evaluator.eval_node(arg) for arg in node.args]))


@LatexVisitor.formats_function('prime')
def format_prime(node, visitor):
number = sympy.latex(visitor.evaluator.eval_node(node.args[0]))
Expand All @@ -161,26 +171,31 @@ def format_prime(node, visitor):
ordinal(int(number)),
r'}\; \mathrm{prime~number}'])


@LatexVisitor.formats_function('isprime')
def format_isprime(node, visitor):
number = sympy.latex(visitor.evaluator.eval_node(node.args[0]))
return ''.join([r'\mathrm{Is~}', number, r'\mathrm{~prime?}'])


@LatexVisitor.formats_function('nextprime')
def format_nextprime(node, visitor):
number = sympy.latex(visitor.evaluator.eval_node(node.args[0]))
return r'\mathrm{Least~prime~greater~than~}' + number


@LatexVisitor.formats_function('factorint')
def format_factorint(node, visitor):
number = sympy.latex(visitor.evaluator.eval_node(node.args[0]))
return r'\mathrm{Prime~factorization~of~}' + number


@LatexVisitor.formats_function('factor')
def format_factor(node, visitor):
expression = sympy.latex(visitor.evaluator.eval_node(node.args[0]))
return r'\mathrm{Factorization~of~}' + expression


@LatexVisitor.formats_function('solve_poly_system')
def format_factorint(node, visitor):
equations = visitor.evaluator.eval_node(node.args[0])
Expand All @@ -194,6 +209,7 @@ def format_factorint(node, visitor):
r'\end{cases} \mathrm{~for~}',
sympy.latex(variables)])


@LatexVisitor.formats_function('plot')
def format_plot(node, visitor):
if node.args:
Expand All @@ -205,6 +221,7 @@ def format_plot(node, visitor):
function = sympy.latex(keywords)
return r'\mathrm{Plot~}' + function


@LatexVisitor.formats_function('rsolve')
def format_rsolve(node, visitor):
recurrence = sympy.latex(sympy.Eq(visitor.evaluator.eval_node(node.args[0]), 0))
Expand All @@ -217,8 +234,11 @@ def format_rsolve(node, visitor):
else:
return r'\mathrm{Solve~the~recurrence~}' + recurrence


diophantine_template = (r"\begin{{align}}&{}\\&\mathrm{{where~}}"
r"{}\mathrm{{~are~integers}}\end{{align}}")


@LatexVisitor.formats_function('diophantine')
def format_diophantine(node, visitor):
expression = visitor.evaluator.eval_node(node.args[0])
Expand All @@ -232,6 +252,7 @@ def format_diophantine(node, visitor):
result = diophantine_template.format(result, tuple(symbols))
return result


@LatexVisitor.formats_function('summation')
@LatexVisitor.formats_function('product')
def format_diophantine(node, visitor):
Expand All @@ -241,13 +262,15 @@ def format_diophantine(node, visitor):
klass = sympy.Product
return sympy.latex(klass(*list(map(visitor.evaluator.eval_node, node.args))))


@LatexVisitor.formats_function('help')
def format_help(node, visitor):
if node.args:
function = visitor.evaluator.eval_node(node.args[0])
return r'\mathrm{Show~documentation~for~}' + function.__name__
return r'\mathrm{Show~documentation~(requires~1~argument)}'


class TopCallVisitor(ast.NodeVisitor):
def __init__(self):
super(TopCallVisitor, self).__init__()
Expand All @@ -264,12 +287,14 @@ def visit_NameConstant(self, node):
if not self.call:
self.call = node


# From https://stackoverflow.com/a/739301/262727
def ordinal(n):
if 10 <= n % 100 < 20:
return 'th'
else:
return {1 : 'st', 2 : 'nd', 3 : 'rd'}.get(n % 10, "th")
return {1: 'st', 2: 'nd', 3: 'rd'}.get(n % 10, "th")


# TODO: modularize all of this
def latexify(string, evaluator):
Expand All @@ -278,13 +303,15 @@ def latexify(string, evaluator):
a.visit(ast.parse(string))
return a.latex


def topcall(string):
a = TopCallVisitor()
a.visit(ast.parse(string))
if hasattr(a, 'call'):
return getattr(a.call.func, 'id', None)
return None


def arguments(string_or_node, evaluator):
node = None
if not isinstance(string_or_node, ast.Call):
Expand Down Expand Up @@ -315,21 +342,20 @@ def arguments(string_or_node, evaluator):
return Arguments(node.value, [], {})
return None


re_calls = re.compile(r'(Integer|Symbol|Float|Rational)\s*\([\'\"]?([a-zA-Z0-9\.]+)[\'\"]?\s*\)')


def re_calls_sub(match):
return match.groups()[1]


def removeSymPy(string):
try:
return re_calls.sub(re_calls_sub, string)
except IndexError:
return string

from sympy.parsing.sympy_parser import (
AppliedFunction, implicit_multiplication, split_symbols,
function_exponentiation, implicit_application, OP, NAME,
_group_parentheses, _apply_functions, _flatten, _token_callable)

def _implicit_multiplication(tokens, local_dict, global_dict):
result = []
Expand Down Expand Up @@ -385,6 +411,7 @@ def _implicit_multiplication(tokens, local_dict, global_dict):
result.append(tokens[-1])
return result


def implicit_multiplication(result, local_dict, global_dict):
"""Makes the multiplication operator optional in most cases.
Expand All @@ -407,6 +434,7 @@ def implicit_multiplication(result, local_dict, global_dict):
result = _flatten(result)
return result


def custom_implicit_transformation(result, local_dict, global_dict):
"""Allows a slightly relaxed syntax.
Expand Down Expand Up @@ -446,6 +474,7 @@ def custom_implicit_transformation(result, local_dict, global_dict):
u'draw': 'plot'
}


def synonyms(tokens, local_dict, global_dict):
"""Make some names synonyms for others.
Expand All @@ -463,6 +492,7 @@ def synonyms(tokens, local_dict, global_dict):
result.append(token)
return result


def close_matches(s, global_dict):
"""
Checks undefined names to see if they are close matches to a defined name.
Expand Down
3 changes: 2 additions & 1 deletion app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import os

from django.core.management.utils import get_random_secret_key
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Expand All @@ -20,7 +21,7 @@
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '3u@n$fij$*cz=sab*xl2j)7ci7qb-e42imrdmw*6#v0%bg_+36'
SECRET_KEY = get_random_secret_key()

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
Expand Down
3 changes: 2 additions & 1 deletion app/test/test_resultsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
from app.logic import resultsets
from sympy import sympify, I, sqrt


def test_predicates():
assert resultsets.is_approximatable_constant(sqrt(2))
assert not resultsets.is_approximatable_constant(sqrt(2))
assert not resultsets.is_approximatable_constant(sympify('2'))
assert resultsets.is_complex(2 * I + 3)
assert not resultsets.is_complex(3)
21 changes: 9 additions & 12 deletions app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
from django.template.loader import render_to_string
from django import forms

# from google.appengine.runtime import DeadlineExceededError

from .constants import LIVE_PROMOTION_MESSAGES, EXAMPLES
from app.logic.logic import SymPyGamma

Expand Down Expand Up @@ -134,6 +132,7 @@ def random_example(request):

return redirect('input/?i=' + six.moves.urllib.parse.quote(random.choice(examples)))


def _process_card(request, card_name):
variable = request.GET.get('variable')
expression = request.GET.get('expression')
Expand Down Expand Up @@ -161,7 +160,8 @@ def eval_card(request, card_name):
return HttpResponse(json.dumps({
'error': str(e)
}), content_type="application/json")
except:
except Exception as e:
logging.error(f'Exception: {e}')
trace = traceback.format_exc(5)
return HttpResponse(json.dumps({
'error': ('There was an error in Gamma. For reference'
Expand All @@ -170,6 +170,7 @@ def eval_card(request, card_name):

return HttpResponse(json.dumps(result), content_type="application/json")


def get_card_info(request, card_name):
g, variable, expression, _ = _process_card(request, card_name)

Expand All @@ -179,11 +180,8 @@ def get_card_info(request, card_name):
return HttpResponse(json.dumps({
'error': str(e)
}), content_type="application/json")
except DeadlineExceededError:
return HttpResponse(json.dumps({
'error': 'Computation timed out.'
}), content_type="application/json")
except:
except Exception as e:
logging.error(f"Exception: {e}")
trace = traceback.format_exc(5)
return HttpResponse(json.dumps({
'error': ('There was an error in Gamma. For reference'
Expand All @@ -192,6 +190,7 @@ def get_card_info(request, card_name):

return HttpResponse(json.dumps(result), content_type="application/json")


def get_card_full(request, card_name):
g, variable, expression, parameters = _process_card(request, card_name)

Expand All @@ -217,10 +216,8 @@ def get_card_full(request, card_name):
},
'input': expression
}), content_type="text/html")
except DeadlineExceededError:
return HttpResponse('Computation timed out.',
content_type="text/html")
except:
except Exception as e:
logging.error(f'Exception: {e}')
trace = traceback.format_exc(5)
return HttpResponse(render_to_string('card.html', {
'cell': {
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ enum34==1.1.10
nose==1.3.7
requests==2.23.0
docutils==0.16
sympy==0.7.5
sympy==1.6

0 comments on commit 20131c6

Please sign in to comment.