diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index d938a29e6ba..e8fc81f4662 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -65,6 +65,11 @@ def mma_free_integrator(expression, v, a=None, b=None): sage: mma_free_integrator(sin(x), x) # optional - internet -cos(x) + A definite integral:: + + sage: mma_free_integrator(e^(-x), x, a=0, b=oo) # optional - internet + 1 + TESTS: Check that :trac:`18212` is resolved:: @@ -72,78 +77,261 @@ def mma_free_integrator(expression, v, a=None, b=None): sage: var('y') # optional - internet y sage: integral(sin(y)^2, y, algorithm='mathematica_free') # optional - internet - -1/2*cos(y)*sin(y) + 1/2*y + 1/2*y - 1/4*sin(2*y) + + :: sage: mma_free_integrator(exp(-x^2)*log(x), x) # optional - internet 1/2*sqrt(pi)*erf(x)*log(x) - x*hypergeometric((1/2, 1/2), (3/2, 3/2), -x^2) + + """ + math_expr = expression._mathematica_init_() + variable = v._mathematica_init_() + if a is None and b is None: + input = "Integrate[{},{}]".format(math_expr, variable) + elif a is not None and b is not None: + input = "Integrate[{},{{{},{},{}}}]".format(math_expr, variable, + a._mathematica_init_(), b._mathematica_init_()) + else: + raise ValueError('a(={}) and b(={}) should be both None' + ' or both defined'.format(a,b)) + json_page_data = request_wolfram_alpha(input) + all_outputs = parse_moutput_from_json(json_page_data) + if not all_outputs: + raise ValueError("no outputs found in the answer from Wolfram Alpha") + first_output = all_outputs[0] + return symbolic_expression_from_mathematica_string(first_output) + +def request_wolfram_alpha(input, verbose=False): + r""" + Request Wolfram Alpha website. + + INPUT: + + - ``input`` -- string + - ``verbose`` -- bool (default: ``False``) + + OUTPUT: + + json + + EXAMPLES:: + + sage: from sage.symbolic.integration.external import request_wolfram_alpha + sage: page_data = request_wolfram_alpha('integrate Sin[x]') # optional internet + sage: [str(a) for a in sorted(page_data.keys())] # optional internet + ['queryresult'] + sage: [str(a) for a in sorted(page_data['queryresult'].keys())] # optional internet + ['datatypes', + 'encryptedEvaluatedExpression', + 'encryptedParsedExpression', + 'error', + 'host', + 'id', + 'numpods', + 'parsetimedout', + 'parsetiming', + 'pods', + 'recalculate', + 'related', + 'server', + 'sponsorCategories', + 'success', + 'timedout', + 'timedoutpods', + 'timing', + 'version'] + """ - import re # import compatible with py2 and py3 - from six.moves.urllib.request import urlopen from six.moves.urllib.parse import urlencode - # We need to integrate against x - vars = [str(x) for x in expression.variables()] - if any(len(x)>1 for x in vars): - raise NotImplementedError("Mathematica online integrator can only handle single letter variables.") - x = SR.var('x') - if repr(v) != 'x': - for i in range(ord('a'), ord('z')+1): - if chr(i) not in vars: - shadow_x = SR.var(chr(i)) - break - expression = expression.subs({x:shadow_x}).subs({v: x}) - params = urlencode({'expr': expression._mathematica_init_(), 'random': 'false'}) - page = urlopen("http://integrals.wolfram.com/home.jsp", params).read() - page = page[page.index('"inputForm"'):page.index('"outputForm"')] - page = re.sub(r"\s", "", page) - mexpr = re.match(r".*Integrate.*==
(.*)

", page).groups()[0] - try: - from sage.libs.pynac.pynac import symbol_table - from sage.interfaces.mathematica import _un_camel as un_camel - from sage.symbolic.constants import constants_name_table as constants - from sage.calculus.calculus import symbolic_expression_from_string - from sage.calculus.calculus import _find_func as find_func - - expr = mexpr.replace('\n',' ').replace('\r', '') - expr = expr.replace('[', '(').replace(']', ')') - expr = expr.replace('{', '[').replace('}', ']') - lsymbols = symbol_table['mathematica'].copy() - autotrans = [str.lower, # Try it in lower case - un_camel, # Convert `CamelCase` to `camel_case` - lambda x: x # Try the original name - ] - # Find the MMA funcs/vars/constants - they start with a letter. - # Exclude exponents (e.g. 'e8' from 4.e8) - p = re.compile(r'(?