diff --git a/AUTHORS b/AUTHORS index 27aefbfac116..f12de84d5585 100644 --- a/AUTHORS +++ b/AUTHORS @@ -191,3 +191,6 @@ Carsten Knoll M R Bharath Matthias Toews Jorge E. Cardona +Sanket Agarwal +Manoj Babu K. +Sai Nikhil diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index ea4320494323..e4b2f7faee55 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -13,9 +13,6 @@ bin/coverage_doctest.py sympy/core/basic.py -This script is based on the sage-coverage script from Sage written by -William Stein. - """ from __future__ import with_statement @@ -23,160 +20,456 @@ import os import re import sys +import string +import inspect from optparse import OptionParser -def parse_file(file, verbose=False): - skipped = [] - missing_docstring = [] - missing_doctest = [] - has_doctest = [] - indirect_doctest = [] - while True: - i = file.find("def ") - if i == -1: - break - - e = re.compile('\)\s*:') - m = e.search(file[i:]) - if m is None: - break - j = m.end() + i - - # "j" now points to the end of the function definition. - - function_name = (' '.join(file[i:j].lstrip('def').split()))[:-1] - bare_function_name = function_name[:function_name.find("(")] - - skip_this = False - for skip in ['__dealloc__', '__new__', '_']: - if function_name.startswith(skip + '('): - skip_this = True - break - if function_name.startswith("_"): - # For the time being, let's skip all "private" functions, that - # beging with "_". Later, when our doctests are in a good shape, we - # may doctest those too. - skip_this = True - if skip_this: - if verbose: - skipped.append(function_name) - file = file[j:] - continue - - k = file[j:].find('\n') - if k == -1: - break - k += j - kk = file[k+1:].find('\n') - if kk == -1: - break - kk += k+1 - - q0 = file[k:kk].find('"""') - if q0 == -1: - missing_docstring.append(function_name) +# Load color templates, used from sympy/utilities/runtests.py +color_templates = ( + ("Black" , "0;30"), + ("Red" , "0;31"), + ("Green" , "0;32"), + ("Brown" , "0;33"), + ("Blue" , "0;34"), + ("Purple" , "0;35"), + ("Cyan" , "0;36"), + ("LightGray" , "0;37"), + ("DarkGray" , "1;30"), + ("LightRed" , "1;31"), + ("LightGreen" , "1;32"), + ("Yellow" , "1;33"), + ("LightBlue" , "1;34"), + ("LightPurple" , "1;35"), + ("LightCyan" , "1;36"), + ("White" , "1;37"), ) + +colors = {} + +for name, value in color_templates: + colors[name] = value +c_normal = '\033[0m' +c_color = '\033[%sm' + +def print_header(name, underline=None, overline=None): + + print + print name + if underline: print underline*len(name) + +def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, score, total_doctests, total_members, verbose=False, no_color=False): + + """ Prints details (depending on verbose) of a module """ + + if no_color: + score_string = "%s%% (%s of %s)" % (score, total_doctests, \ + total_members) + + elif score < 100: + score_string = "%s%s%% (%s of %s)%s" % (c_color % (colors["Red"]) \ + , score , total_doctests, total_members, c_normal) + else: + score_string = "%s%s%% (%s of %s)%s" % (c_color % (colors["Green"]) \ + , score , total_doctests, total_members, c_normal) + + if verbose: + print '\n'+'-'*70 + print module_path + print '-'*70 + else: + print "%s: %s" % (module_path, score_string) + + if verbose: + print_header('CLASSES', '*') + if not c: + print_header('No classes found!') + else: - q0 += k - q1 = file[q0+3:].find('"""') - if q1 == -1: - print "ERROR: Error parsing %s" % function_name - else: - q1 += q0 + 3 - # the docstring is now between q0:q1 - d = file[q0:q1].find('>>>') - if d == -1: - missing_doctest.append(function_name) - else: - has_doctest.append(function_name) - if not (bare_function_name[0:2] == '__' and - bare_function_name[-2:] == '__'): - d = file[q0:q1].find(bare_function_name) - e = file[q0:q1].find('indirect doctest') - if d == -1 and e == -1: - indirect_doctest.append(function_name) - - file = file[j+3:] - return skipped, missing_docstring, missing_doctest, has_doctest, \ - indirect_doctest - - -def coverage(filename, file, verbose=False): - skipped, missing_docstring, missing_doctest, has_doctest, \ - indirect_doctest = parse_file(file, verbose) - num_functions = len(missing_docstring + missing_doctest + has_doctest) - if num_functions == 0: - print "No functions in %s" % filename + if c_md: + print_header('Missing docstrings','-') + for md in c_md: + print ' * '+md + if c_mdt: + print_header('Missing doctests','-') + for md in c_mdt: + print ' * '+md + if c_idt: + # Use "# indirect doctest" in the docstring to + # supress this warning. + print_header('Indirect doctests', '-') + for md in c_idt: + print ' * '+md + print '\n Use \"# indirect doctest\" in the docstring to supress this warning' + + + print_header('FUNCTIONS','*') + if not f: + print_header('No functions found!') + else: + if f_md: + print_header('Missing docstrings', '-') + for md in f_md: + print ' * '+md + if f_mdt: + print_header('Missing doctests', '-') + for md in f_mdt: + print ' * '+md + if f_idt: + print_header('Indirect doctests', '-') + for md in f_idt: + print ' * '+md + print '\n Use \"# indirect doctest\" in the docstring to supress this warning' + + + if verbose: + print '\n'+'-'*70 + print "SCORE: %s" % (score_string) + print '-'*70 + + +def _is_indirect(member, doc): + + """ Given string repr of doc and member checks if the member + contains indirect documentation """ + + d = member in doc + e = 'indirect doctest' in doc + if not d and not e: + return True + else: + return False + +def _get_arg_list(name, fobj): + + """ Given a function object, constructs a list of arguments + and their defaults. Takes care of varargs and kwargs """ + + trunc = 20 # Sometimes argument length can be huge + + argspec = inspect.getargspec(fobj) + + arg_list = [] + + if argspec.args: + for arg in argspec.args: arg_list.append(str(arg)) + + arg_list.reverse() + + # Now add the defaults + if argspec.defaults: + rev_defaults = list(argspec.defaults).reverse() + for i in range(len(argspec.defaults)): + arg_list[i] = str(arg_list[i]) + '=' + str(argspec.defaults[-i]) + + # Get the list in right order + arg_list.reverse() + + # Add var args + if argspec.varargs: + arg_list.append(argspec.varargs) + if argspec.keywords: + arg_list.append(argspec.keywords) + + # Truncate long arguments + arg_list = map(lambda x: x[:trunc], arg_list) + + # Construct the parameter string (enclosed in brackets) + str_param = "%s(%s)" % (name, ', '.join(arg_list)) + + return str_param + +def get_mod_name(path, base): + + """ Gets a module name, given the path of file/dir and base + dir of sympy """ + + rel_path = os.path.relpath(path, base) + + # Remove the file extension + rel_path, ign = os.path.splitext(rel_path) + + # Replace separators by . for module path + file_module = "" + h, t = os.path.split(rel_path) + while h or t: + if t: file_module = t + '.' + file_module + h, t = os.path.split(h) + + return file_module[:-1] + + + +def process_function(name, c_name, b_obj, mod_path, f_sk, f_md, f_mdt, f_idt, f_has_doctest, sk_list): + + """ Processes a function to get information regarding documentation. + It is assume that the function calling this subrouting has already + verified that it is a valid module function """ + + if name in sk_list: return False, False + + # We add in the end, as inspect.getsourcelines is slow + add_md = False + add_mdt = False + add_idt = False + f_doctest = False + function = False + + if inspect.isclass(b_obj): + obj = getattr(b_obj, name) + else: + obj = b_obj + + # Check function for various categories + if inspect.isclass(b_obj): + full_name = _get_arg_list(c_name + '.' + name, obj) + else: + full_name = _get_arg_list(name, obj) + if name.startswith('_'): f_sk.append(full_name) + else: + if not obj.__doc__: + add_md = True + elif not '>>>' in obj.__doc__: + add_mdt = True + elif _is_indirect(name, obj.__doc__): + add_idt = True + else: f_doctest = True + + function = True + + if add_md or add_mdt or add_idt: + + try: + line_no = inspect.getsourcelines(obj)[1] + except IOError: + # Raised when source does not exist + # which means the function is not there. + return False, False + + full_name = "LINE %d: %s" % (line_no, full_name) + if add_md: f_md.append(full_name) + elif add_mdt: f_mdt.append(full_name) + elif add_idt: f_idt.append(full_name) + + return f_doctest, function + + +def process_class(c_name, obj, c_sk, c_md, c_mdt, c_idt, c_has_doctest): + + """ Extracts information about the class regarding documentation. + It is assumed that the function calling this subroutine has already + checked that the class is valid. """ + + # Skip class case + if c_name.startswith('_'): + c_sk.append(c_name) + return False, False, None + + c = False + c_dt = False + # Get the line number of class + try: + source, line_no = inspect.getsourcelines(obj) + except IOError: + # Raised when source does not exist + # which means the class is not there. + return False, False, None + + c = True + full_name = "LINE %d: %s" % (line_no, c_name) + if not obj.__doc__: c_md.append(full_name) + elif not '>>>' in obj.__doc__: c_mdt.append(full_name) + elif _is_indirect(c_name, obj.__doc__): + c_idt.append(full_name) + else: + c_dt = True + c_has_doctest.append(full_name) + + return c_dt, c, source + +def coverage(module_path, verbose=False, no_color=False): + + """ Given a module path, builds an index of all classes and functions + contained. It then goes through each of the classes/functions to get + the docstring and doctest coverage of the module. """ + + # Import the package and find membmers + m = None + try: + __import__(module_path) + m = sys.modules[module_path] + except Exception, a: + # Most likely cause, absence of __init__ + print "%s could not be loaded due to %s." % (module_path, repr(a)) return 0, 0 - print '-'*70 - print filename - doctests = len(has_doctest) - score = 100 * float(doctests) / num_functions - score = int(score) - if missing_docstring: - print "\nMissing documentation:\n\t * %s\n" % \ - ('\n\t * '.join(missing_docstring)) - if missing_doctest: - print "\nMissing doctests:\n\t * %s\n" % \ - ('\n\t * '.join(missing_doctest)) - if indirect_doctest: - print "\nIndirect doctest (function name doesn't occur in doctests):\n"\ - "\t * %s\n"%('\n\t * '.join(indirect_doctest)) - print 'Use "# indirect doctest" in the docstring to surpress this ' \ - 'warning' + c_skipped = [] + c_md = [] + c_mdt = [] + c_has_doctest = [] + c_idt = [] + classes = 0 + c_doctests = 0 + + f_skipped = [] + f_md = [] + f_mdt = [] + f_has_doctest = [] + f_idt = [] + functions = 0 + f_doctests = 0 + + skip_members = ['__abstractmethods__'] + + # Get the list of members + m_members = dir(m) + for member in m_members: + + # Check for skipped functions first, they throw nasty errors + # when combined with getattr + if member in skip_members: continue - print "SCORE %s: %s%% (%s of %s)" % (filename, score, doctests, num_functions) + # Identify if the member (class/def) a part of this module + obj = getattr(m, member) + obj_mod = inspect.getmodule(obj) - print '-'*70 + # Function not a part of this module + if not obj_mod or not obj_mod.__name__ == module_path: + continue - return len(has_doctest), num_functions + # If it's a function + if inspect.isfunction(obj) or inspect.ismethod(obj): + f_dt, f = process_function(member, '', obj, module_path, f_skipped, f_md, f_mdt, f_idt, f_has_doctest, skip_members) + if f: functions += 1 + if f_dt: f_doctests += 1 + # If it's a class, look at it's methods too + elif inspect.isclass(obj): + + # Process the class first + c_dt, c, source = process_class(member, obj, c_skipped, c_md, c_mdt, c_idt, c_has_doctest) + if not c: continue + else: classes += 1 + if c_dt: c_doctests += 1 + + # Iterate through it's members + for f_name in obj.__dict__: + + if f_name in skip_members or f_name.startswith('_'): continue + + # Check if def funcname appears in source + if not ("def "+f_name) in ' '.join(source): continue + + # Identify the module of the current class member + f_obj = getattr(obj, f_name) + obj_mod = inspect.getmodule(f_obj) + + # Function not a part of this module + if not obj_mod or not obj_mod.__name__ == module_path: + continue + + # If it's a function + if inspect.isfunction(f_obj) or inspect.ismethod(f_obj): + + f_dt, f = process_function(f_name, member, obj, module_path, f_skipped, f_md, f_mdt, f_idt, f_has_doctest, skip_members) + if f: functions += 1 + if f_dt: f_doctests += 1 + + + # Evaluate the percent coverage + total_doctests = c_doctests + f_doctests + total_members = classes + functions + if total_members: score = 100 * float(total_doctests) / (total_members) + else: score = 100 + score = int(score) + + # Sort functions/classes by line number + c_md = sorted(c_md, key = lambda x: int(x.split()[1][:-1])) + c_mdt = sorted(c_mdt, key = lambda x: int(x.split()[1][:-1])) + c_idt = sorted(c_idt, key = lambda x: int(x.split()[1][:-1])) + + f_md = sorted(f_md, key = lambda x: int(x.split()[1][:-1])) + f_mdt = sorted(f_mdt, key = lambda x: int(x.split()[1][:-1])) + f_idt = sorted(f_idt, key = lambda x: int(x.split()[1][:-1])) + + print_coverage(module_path, classes, c_md, c_mdt, c_idt, functions, f_md, f_mdt, f_idt, score, total_doctests, total_members, verbose, no_color) + + + return total_doctests, total_members + +def go(sympy_top, file, verbose=False, no_color=False, exact=True): -def go(file, verbose=False, exact=True): if os.path.isdir(file): doctests, num_functions = 0, 0 for F in os.listdir(file): - _doctests, _num_functions = go('%s/%s'%(file,F), verbose, exact=False) + _doctests, _num_functions = go(sympy_top, '%s/%s'%(file,F), verbose, no_color, exact=False) doctests += _doctests num_functions += _num_functions return doctests, num_functions if not (file.endswith('.py') or file.endswith('.pyx')) or \ + file.endswith('__init__.py') or \ not exact and ('test_' in file or 'bench_' in file): return 0, 0 if not os.path.exists(file): print "File %s does not exist."%file sys.exit(1) - with open(file) as fh: - f = fh.read() - return coverage(file, f, verbose) + + # Relpath for constructing the module name + return coverage(get_mod_name(file, sympy_top), verbose, no_color) + if __name__ == "__main__": + + bintest_dir = os.path.abspath(os.path.dirname(__file__)) # bin/cover... sympy_top = os.path.split(bintest_dir)[0] # ../ sympy_dir = os.path.join(sympy_top, 'sympy') # ../sympy/ if os.path.isdir(sympy_dir): sys.path.insert(0, sympy_top) - parser = OptionParser() + usage = "usage: ./bin/doctest_coverage.py PATH" + + parser = OptionParser( + description = __doc__, + usage = usage, + ) + parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False) + parser.add_option("--no-color", action="store_true", dest="no_color", help="use no colors", default=False) options, args = parser.parse_args() + # If no arguments, then run script on sympy/ if len(args) == 0: - parser.print_help() - else: - for file in args: - doctests, num_functions = go(file, options.verbose) - if num_functions == 0: - score = 100 - else: - score = 100 * float(doctests) / num_functions - score = int(score) - print - print '='*70 + args = [os.path.join(sympy_top, 'sympy')] + + for file in args: + file = os.path.normpath(file) + print 'DOCTEST COVERAGE for %s' % (file) + print '='*70 + print + doctests, num_functions = go(sympy_top, file, options.verbose, options.no_color) + if num_functions == 0: + score = 100 + else: + score = 100 * float(doctests) / num_functions + score = int(score) + print + print '='*70 + + if options.no_color: print "TOTAL SCORE for %s: %s%% (%s of %s)" % \ - (file, score, doctests, num_functions) - print + (get_mod_name(file, sympy_top),score, doctests, num_functions) + + elif score < 100: + print "TOTAL SCORE for %s: %s%s%% (%s of %s)%s" % \ + (get_mod_name(file, sympy_top), c_color % (colors["Red"]),\ + score, doctests, num_functions, c_normal) + + else: + print "TOTAL SCORE for %s: %s%s%% (%s of %s)%s" % \ + (get_mod_name(file, sympy_top), c_color % (colors["Green"]),\ + score, doctests, num_functions, c_normal) + + print diff --git a/doc/Makefile b/doc/Makefile index 5d00d063190a..4050b58a460e 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -28,6 +28,7 @@ help: @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " cheatsheet to make the Cheatsheet" + @echo " htmli18n to make the i18n versions" clean: -rm -rf _build diff --git a/doc/src/aboutus.txt b/doc/src/aboutus.txt index fddf058c59f8..3991c953d773 100644 --- a/doc/src/aboutus.txt +++ b/doc/src/aboutus.txt @@ -195,9 +195,12 @@ want to be mentioned here, so see our repository history for a full list). #. Nathan Alison: Additions to the stats module #. Christian Bühler: remove use of set_main() in GA #. Carsten Knoll: improvement to preview() -#. M R Bharath: modified use of int_tested +#. M R Bharath: modified use of int_tested, improvement to Permutation #. Matthias Toews: File permissions #. Jorge E. Cardona: Cleanup in polys +#. Sanket Agarwal: Rewrite coverage_doctest.py script +#. Manoj Babu K.: Improve gamma function +#. Sai Nikhil: Fix to Heavyside with complex arguments Up-to-date list in the order of the first contribution is given in the `AUTHORS `_ file. diff --git a/doc/src/modules/evalf.txt b/doc/src/modules/evalf.txt index 3a1f69e47b2b..ac0f4c3edd4e 100644 --- a/doc/src/modules/evalf.txt +++ b/doc/src/modules/evalf.txt @@ -203,28 +203,26 @@ imaginary portions of a number with exact zeros: 3.00000000000000 -In situations where you wish to remove meaningless digits, re-evaluation and -the use of the ``round`` function are useful: +In situations where you wish to remove meaningless digits, re-evaluation or +the use of the ``round`` method are useful: >>> Float('.1', '')*Float('.12345', '') 0.012297 >>> ans = _ >>> N(ans, 1) 0.01 - >>> round(ans, 2) + >>> ans.round(2) 0.01 If you are dealing with a numeric expression that contains no floats, it -will be evaluable to arbitrary precision. To round the result relative to -the decimal, the round function is useful: +can be evaluated to arbitrary precision. To round the result relative to +a given decimal, the round method is useful: - >>> from sympy import round - >>> 10*pi + cos(1) - cos(1) + 10*pi - >>> N(_) + >>> v = 10*pi + cos(1) + >>> N(v) 31.9562288417661 - >>> round(_, 3) + >>> v.round(3) 31.956 diff --git a/doc/src/tutorial.pot b/doc/src/tutorial.pot index 1edf27e7318d..546a2f5425ea 100644 --- a/doc/src/tutorial.pot +++ b/doc/src/tutorial.pot @@ -1,5 +1,5 @@ # SOME DESCRIPTIVE TITLE. -# Copyright (C) 2008, 2009, 2010, 2011 SymPy Development Team +# Copyright (C) 2008, 2009, 2010, 2011, 2012 SymPy Development Team # This file is distributed under the same license as the SymPy package. # FIRST AUTHOR , YEAR. # @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: SymPy 0.7.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-12-27 11:31\n" +"POT-Creation-Date: 2012-03-19 21:43\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,489 +17,489 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" #: ../../src/tutorial.txt:5 -# 732f9d3ed26b432282e44517d896b4ad +# acb2afa576a345518166bb3c57217833 msgid "Tutorial" msgstr "" #: ../../src/tutorial.txt:10 -# 451cec5489894240af7e5100a4524a91 +# 5baabfea76c0451eb820e80b7febac52 msgid "Introduction" msgstr "" #: ../../src/tutorial.txt:12 -# 216cd1b263104bc78d164fe0421ac597 +# 256d4613c78e46fbb9b76eec0d90c13f msgid "SymPy is a Python library for symbolic mathematics. It aims to become a full-featured computer algebra system (CAS) while keeping the code as simple as possible in order to be comprehensible and easily extensible. SymPy is written entirely in Python and does not require any external libraries." msgstr "" #: ../../src/tutorial.txt:17 -# d83a70daa53f4784b6cdf11bd0dd33fc +# 99f416537eaa4caeb105763393832fc4 msgid "This tutorial gives an overview and introduction to SymPy. Read this to have an idea what SymPy can do for you (and how) and if you want to know more, read the :ref:`SymPy User's Guide `, :ref:`SymPy Modules Reference `. or the `sources `_ directly." msgstr "" #: ../../src/tutorial.txt:26 -# 161fc1f31a1d4e9c8471f9e09e493121 +# 84a7cb2d0b284a2f9c22d0a0a346b46e msgid "First Steps with SymPy" msgstr "" #: ../../src/tutorial.txt:28 -# f6b5e99d3f64476d9f69b12afbfe8e3b +# 5c43ba2c58c144a19a9033ecf0df33db msgid "The easiest way to download it is to go to http://code.google.com/p/sympy/ and download the latest tarball from the Featured Downloads:" msgstr "" #: ../../src/tutorial.txt:34 -# 2a02bd794e3c4a6ea9807f250f53711e +# ef2ed50a709b48bfa1b02b340e564281 msgid "Unpack it:" msgstr "" #: ../../src/tutorial.txt:40 -# 9fc671cc83204e07b741da4694f7c7c9 +# 2b7bd9f706ec413fbd7193e39a317e39 msgid "and try it from a Python interpreter:" msgstr "" #: ../../src/tutorial.txt:54 -# 09403e8ab4c54f30aa079e0b2234ea79 +# a94bf4c4986242d68bbe0c8bd492fe9d msgid "You can use SymPy as shown above and this is indeed the recommended way if you use it in your program. You can also install it using ``./setup.py install`` as any other Python module, or just install a package in your favourite Linux distribution, e.g.:" msgstr "" #: ../../src/tutorial.txt:80 -# ec4495a7a9644e09a36bd92f29bcf870 +# db569c3d059a46dbbc862a312b1f2e69 msgid "For other means how to install SymPy, consult the Downloads_ tab on the SymPy's webpage." msgstr "" #: ../../src/tutorial.txt:87 -# 348ffcf2a09d4394b68a9b5b0fc8b3ac +# b6613cfc82394874b3b472dc0035a204 msgid "isympy Console" msgstr "" #: ../../src/tutorial.txt:89 -# 557bf435e2484728b5d4fdca36e8e42c +# cabd8f1c87e84121b2c53294a91d0fff msgid "For experimenting with new features, or when figuring out how to do things, you can use our special wrapper around IPython called ``isympy`` (located in ``bin/isympy`` if you are running from the source directory) which is just a standard Python shell that has already imported the relevant SymPy modules and defined the symbols x, y, z and some other things:" msgstr "" #: ../../src/tutorial.txt:119 -# 458f99359b0248b2b55c3f2c037e64b8 +# dd06236a16fb405f8f320e937973995c msgid "Commands entered by you are bold. Thus what we did in 3 lines in a regular Python interpreter can be done in 1 line in isympy." msgstr "" #: ../../src/tutorial.txt:124 -# 24f82584e8d444fa8b0107d439739fd6 +# 79bd128425cc425788ebc134eebf53a3 msgid "Using SymPy as a calculator" msgstr "" #: ../../src/tutorial.txt:126 -# 93ee0aedb162492488b81fe5070a9585 +# dfe8115d5190405b82fc6cd364f0fc36 msgid "SymPy has three built-in numeric types: Float, Rational and Integer." msgstr "" #: ../../src/tutorial.txt:128 -# 0c25776a46fc414f930c2871c7e8546f +# 6c434664bcd84c0db77cc45f7e7db316 msgid "The Rational class represents a rational number as a pair of two Integers: the numerator and the denominator. So Rational(1,2) represents 1/2, Rational(5,2) represents 5/2, and so on." msgstr "" #: ../../src/tutorial.txt:147 -# e98915d1c2754ad2b41f796f9688bf05 +# 002acad721be4930b26f5a64ec0cf0f9 msgid "Proceed with caution while working with Python int's and floating point numbers, especially in division, since you may create a Python number, not a SymPy number. A ratio of two Python ints may create a float -- the \"true division\" standard of Python 3 and the default behavior of ``isympy`` which imports division from __future__::" msgstr "" #: ../../src/tutorial.txt:157 -# b29c17efeeea4a6180132ff9a66b52bf +# d68deed1923745968dc0bfd723c0efa0 msgid "But in earlier Python versions where division has not been imported, a truncated int will result::" msgstr "" #: ../../src/tutorial.txt:163 -# 0b7c0141342341b7a04392fac4299f47 +# f6a1edaf20e1445f89642eb241a92b64 msgid "In both cases, however, you are not dealing with a SymPy Number because Python created its own number. Most of the time you will probably be working with Rational numbers, so make sure to use Rational to get the SymPy result. One might find it convenient to equate ``R`` and Rational::" msgstr "" #: ../../src/tutorial.txt:175 -# d285d5d945264083a9e49ffaf31de757 +# 3ad2e14a17f149908233527b24567962 msgid "We also have some special constants, like e and pi, that are treated as symbols (1+pi won't evaluate to something numeric, rather it will remain as 1+pi), and have arbitrary precision::" msgstr "" #: ../../src/tutorial.txt:189 -# 8a163496035641a993d7fd2008859e72 +# 153d98df535b47bd9edf6083360c0551 msgid "as you see, evalf evaluates the expression to a floating-point number" msgstr "" #: ../../src/tutorial.txt:191 -# c1301431a4864fe58a43cf04912b2915 +# afd1a1b9739a4d6dbbb914c0453200a7 msgid "The symbol ``oo`` is used for a class defining mathematical infinity::" msgstr "" #: ../../src/tutorial.txt:200 -# 6e6ea47d05864226be26fd59c5473686 +# 334ecf48603f4c189d3ec327ca7d4cc4 msgid "Symbols" msgstr "" #: ../../src/tutorial.txt:202 -# 3c9ca1ace4564219b7c0431efd16fe58 +# 203d8c8ba0984ca9bf2e63debce2eb88 msgid "In contrast to other Computer Algebra Systems, in SymPy you have to declare symbolic variables explicitly::" msgstr "" #: ../../src/tutorial.txt:209 -# 36c1664c83eb4627988ceada02665c9e +# 26b815b542f7475b83838528c500f49e msgid "On the left is the normal Python variable which has been assigned to the SymPy Symbol class. Predefined symbols (including those for symbols with Greek names) are available for import from abc:" msgstr "" #: ../../src/tutorial.txt:215 -# 4b6a9120a0484d06b47f8101bdc90f9f +# 6bba571e2ea1447fa12afbbee7f54321 msgid "Symbols can also be created with the ``symbols`` or ``var`` functions, the latter automatically adding the created symbols to the namespace, and both accepting a range notation:" msgstr "" #: ../../src/tutorial.txt:227 -# b4f2036700724c3a8839b348b70b5de3 +# 3700a3b5afc946c3affd750951b270bc msgid "Instances of the Symbol class \"play well together\" and are the building blocks of expresions::" msgstr "" #: ../../src/tutorial.txt:239 -# e840044ae15c464b886acd21e8caba8f +# fe0f13bf02244b3b9a661b1dc7c6170b msgid "They can be substituted with other numbers, symbols or expressions using ``subs(old, new)``::" msgstr "" #: ../../src/tutorial.txt:250 -# 8fdb772f0bd74b239e80d02be3ffe66d +# aa339bf69f064cddaf336848621cf721 msgid "For the remainder of the tutorial, we assume that we have run::" msgstr "" #: ../../src/tutorial.txt:255 -# 39f6c80bd03c4f1392811002fb712001 +# 178eaff4af8749f5ab038038df2dd979 msgid "This will make things look better when printed. See the :ref:`printing-tutorial` section below. If you have a unicode font installed, you can pass use_unicode=True for a slightly nicer output." msgstr "" #: ../../src/tutorial.txt:260 -# 12e95a8aad024ee4a420d82480c75a61 +# a13f672e86c44a88b7dc25d9b6cc3554 msgid "Algebra" msgstr "" #: ../../src/tutorial.txt:262 -# d2a06592066142ef93bed65b82f00fa6 +# bca5a086f8404a3aa9db07cde1c8e2c5 msgid "For partial fraction decomposition, use ``apart(expr, x)``::" msgstr "" #: ../../src/tutorial.txt:287 -# 04498552e4da4c059574153a4999d788 +# ecfa869994b54b13bb1a8409e57eb3d1 msgid "To combine things back together, use ``together(expr, x)``::" msgstr "" #: ../../src/tutorial.txt:309 -# bdfc6dda6c0f48d284e1bf5e3ff29fb4 +# 4251bdd8c35749ab941e692003e21419 msgid "Calculus" msgstr "" #: ../../src/tutorial.txt:314 -# ac43b6ece2494f929c2de8f565ef8519 +# df475b8d7bc64beab057a8f43904199f msgid "Limits" msgstr "" #: ../../src/tutorial.txt:316 -# 85cea25004744584939528fdc6a7190e +# fe99cf124bc847508c6509afda81c8bf msgid "Limits are easy to use in SymPy, they follow the syntax ``limit(function, variable, point)``, so to compute the limit of f(x) as x -> 0, you would issue ``limit(f, x, 0)``::" msgstr "" #: ../../src/tutorial.txt:325 -# ca8dfba01c154f5ead42a4178d35f18a +# d661002f90504b2ab3e8c5dbd484278f msgid "you can also calculate the limit at infinity::" msgstr "" #: ../../src/tutorial.txt:336 -# 228ec397b9a045b3a73040538b911341 +# 780ee445798a42f888e97f5f5b65fc90 msgid "for some non-trivial examples on limits, you can read the test file `test_demidovich.py `_" msgstr "" #: ../../src/tutorial.txt:343 -# 1c46646434ce440295ab7c3d1db1bef7 +# 2b0afacfd36c474192de39089d72a1ad msgid "Differentiation" msgstr "" #: ../../src/tutorial.txt:345 -# c4bff6afce3d4af19fbf87f9e39bf1df +# ee1edf8a27214e8885868c1a0766470a msgid "You can differentiate any SymPy expression using ``diff(func, var)``. Examples::" msgstr "" #: ../../src/tutorial.txt:358 -# e38529123bdd468dae60455a149cd84f +# a2bf155b48374e3594bca85a05df6bcf msgid "You can check, that it is correct by::" msgstr "" #: ../../src/tutorial.txt:366 -# cfef8fbe570e4d5cb9173bb038f7efe9 +# 2166c7b1c03643798513321c316bf0cb msgid "Higher derivatives can be calculated using the ``diff(func, var, n)`` method::" msgstr "" #: ../../src/tutorial.txt:383 -# f1b8daab34c04b6fbc37240e0bd76020 +# 379428fb2f94470abe0848f4e92cb247 msgid "Series expansion" msgstr "" #: ../../src/tutorial.txt:385 -# e6d79e7341304d809805c6ec3091d251 +# 28ca52da921c4bf78eeb0c18d1a54471 msgid "Use ``.series(var, point, order)``::" msgstr "" #: ../../src/tutorial.txt:400 -# 285cd15cc0f34583a70a30ef9f02792e +# a0c071244b61480ba63985c91817b685 msgid "Another simple example::" msgstr "" #: ../../src/tutorial.txt:420 -# 433a7dbd128547f0a0da4f64611d74f4 +# 9c24d99f0b00409eb134ed013b67f4f7 msgid "Integration" msgstr "" #: ../../src/tutorial.txt:422 -# 71ba9e9e3acb4a18b8e66e4d77a2af0e +# f7c073ebb0cc44dabf438cf42570b6a5 msgid "SymPy has support for indefinite and definite integration of transcendental elementary and special functions via ``integrate()`` facility, which uses powerful extended Risch-Norman algorithm and some heuristics and pattern matching::" msgstr "" #: ../../src/tutorial.txt:430 -# fc3e70a6d780443e9bf3109bc652e240 +# e7ecae8925b7460892e7bd2141667f2b msgid "You can integrate elementary functions::" msgstr "" #: ../../src/tutorial.txt:443 -# 814aef8e8a8e4880be4d6c7d4b1bf9fd +# a9e5e0ed9d2247438b19682f1ec6aa03 msgid "Also special functions are handled easily::" msgstr "" #: ../../src/tutorial.txt:451 -# 2c946788706b442bbdd2460331b16336 +# f96ec839ce31484b8c16c824ac6a4520 msgid "It is possible to compute definite integrals::" msgstr "" #: ../../src/tutorial.txt:460 -# a0490854b02f451eb9e79e0ac318a81e +# b0d4b46fe22d41f7a338db6897d3add2 msgid "Also, improper integrals are supported as well::" msgstr "" #: ../../src/tutorial.txt:472 -# cad74929c25e4bd6bf5806a2ea818c12 +# 6321cb7e2fe94a448f6fb27f2e8cd8cb msgid "Complex numbers" msgstr "" #: ../../src/tutorial.txt:474 -# ebb5ccfc3a6a4bc0b815ccb8d1206864 +# 93f451df075443dab1f2867bf168751e msgid "Besides the imaginary unit, I, which is imaginary, symbols can be created with attributes (e.g. real, positive, complex, etc...) and this will affect how they behave::" msgstr "" #: ../../src/tutorial.txt:491 -# aff4acd48154490e972d86f47f28863f +# b09fb8f51af34a348cda062f128d66be msgid "Functions" msgstr "" #: ../../src/tutorial.txt:493 -# 684004bca075439793f20dfaaaa42b45 +# 55de5a5342d4441bbd191529542c96ec msgid "**trigonometric**::" msgstr "" #: ../../src/tutorial.txt:542 -# 58122a0a31014fa38bc20727e2bac91a +# 1bcfeed1b8554a97954663ff538dd316 msgid "**spherical harmonics**::" msgstr "" #: ../../src/tutorial.txt:568 -# bf7e09f102e84dd88bd7eaae5505b87f +# f1e7c5d9b4fe4393a3011b0b56bd8938 msgid "**factorials and gamma function**::" msgstr "" #: ../../src/tutorial.txt:586 -# 64f5238c270c470db1e3c6ed80a9487a +# 8d0f515273eb4ac9a7b2cc0075e07018 msgid "**zeta function**::" msgstr "" #: ../../src/tutorial.txt:611 -# bb26f501df4c4e818d3126e71c2a5c0b +# 4a2d217964744fe2928a275a18c54dc7 msgid "**polynomials**::" msgstr "" #: ../../src/tutorial.txt:650 -# a130917123e34abcab234cf75b426603 +# aef9c047b3fd498e8e3387291a5a1642 msgid "Differential Equations" msgstr "" #: ../../src/tutorial.txt:652 #: ../../src/tutorial.txt:672 -# 057ab739db5f4bd59ccc2f00207f4198 -# a5d758cbefce49919af460634d0c49d6 +# 3fd312fab75a4e0093c08194a273ea89 +# dca723333a574cb4a1f564a8e6c54354 msgid "In ``isympy``::" msgstr "" #: ../../src/tutorial.txt:670 -# d4a9d00981b144e6a98059ab9b2df962 +# c3c7cd30f58d44bc80fbe81fa54436d6 msgid "Algebraic equations" msgstr "" #: ../../src/tutorial.txt:685 -# de2553d2167543d8898f2026635449fd +# 1e6f6baa8df24cf8b0a39515ca464b91 msgid "Linear Algebra" msgstr "" #: ../../src/tutorial.txt:690 -# deb8023b9b8744e6aa1995d09b7cfa31 +# ae52076e96b64fdda18bdf97b346aac5 msgid "Matrices" msgstr "" #: ../../src/tutorial.txt:692 -# 9c0f45ed13df49799464cf5d09761de6 +# e6ebc1773b60428e98b9a7e1a4800b5f msgid "Matrices are created as instances from the Matrix class::" msgstr "" #: ../../src/tutorial.txt:700 -# eadb65467502490c93d85bb3de0a8ce1 +# db1deabb15674847b005ce3c4c198cf9 msgid "They can also contain symbols::" msgstr "" #: ../../src/tutorial.txt:715 -# 29daae2f52584517b13a03d96948b1fc +# a3440bf0d51b4596aa888e0f0b810ba6 msgid "For more about Matrices, see the Linear Algebra tutorial." msgstr "" #: ../../src/tutorial.txt:720 -# 7d4d3ae5599447bc9bfdfdad42737640 +# 2b8557bf9df44b4c8f06cd7eaf2abaed msgid "Pattern matching" msgstr "" #: ../../src/tutorial.txt:722 -# 9c75a5ecf81d4680a414a33d885ddac7 +# cf238a9950824aff8e528bf76dadd4af msgid "Use the ``.match()`` method, along with the ``Wild`` class, to perform pattern matching on expressions. The method will return a dictionary with the required substitutions, as follows::" msgstr "" #: ../../src/tutorial.txt:736 -# d77b7872bbc84728b18ce7fed515ad06 +# b75925954afc46a3b7195e9ee6a89466 msgid "If the match is unsuccessful, it returns ``None``::" msgstr "" #: ../../src/tutorial.txt:741 -# 67cfa0de535249cf84e9d5cc07b1a23c +# 0c50b98aa50643089982037b5dda92a5 msgid "One can also use the exclude parameter of the ``Wild`` class to ensure that certain things do not show up in the result::" msgstr "" #: ../../src/tutorial.txt:755 -# d0e3404bfd804969a5aca6bb792b3c87 +# fcc2a34a2f9e43258b4b6090e61fd45f msgid "Printing" msgstr "" #: ../../src/tutorial.txt:757 -# a4b1d024ca604d2d852bf5211fd193cc +# ad0c1c0213d141048aadf3b8cf0b3fb4 msgid "There are many ways to print expressions." msgstr "" #: ../../src/tutorial.txt:759 -# 9f4342fd36bd4c22885a1bc3dcd9459c +# 2603e8ebbba642a797c60a563e0c92ea msgid "**Standard**" msgstr "" #: ../../src/tutorial.txt:761 -# 61c7daed76664396a0dd8976ff8c5940 +# 1b73d51478cb4466a7dcda1599489c5d msgid "This is what ``str(expression)`` returns and it looks like this:" msgstr "" #: ../../src/tutorial.txt:772 -# ccb50a3c17504313b6fa0aa92c6907cd +# 6bd6e11ccb534aeb9a01cb5f67089f88 msgid "**Pretty printing**" msgstr "" #: ../../src/tutorial.txt:774 -# 9021e89423f8414e80939d5f3e125096 +# 67a9df506d4d44e1b897a9a69d726e42 msgid "Nice ascii-art printing is produced by the ``pprint`` function:" msgstr "" #: ../../src/tutorial.txt:793 -# 359a87d92c1c44b69336b280c0601aca +# efdfdfe2a5a84737ad2be06fb637b762 msgid "If you have a unicode font installed, the ``pprint`` function will use it by default. You can override this using the ``use_unicode`` option.:" msgstr "" #: ../../src/tutorial.txt:803 -# 639a75476bf848d1a0b5478a386817a8 +# cd4ba698122b445885fe984528f5e3c6 msgid "See also the wiki `Pretty Printing `_ for more examples of a nice unicode printing." msgstr "" #: ../../src/tutorial.txt:807 -# d9cce198e31147589771b41b22974314 +# 02454b004b47443385015dda6669e772 msgid "Tip: To make pretty printing the default in the Python interpreter, use::" msgstr "" #: ../../src/tutorial.txt:830 -# 2950916b6f4f48e6b54250925e6a1051 +# e0f14bff30fd409392c23d90d2743c62 msgid "**Python printing**" msgstr "" #: ../../src/tutorial.txt:846 -# f5682ca72f04469090098ea039525467 +# c367d0cba00d478ea14ad5063c44c1b5 msgid "**LaTeX printing**" msgstr "" #: ../../src/tutorial.txt:863 -# 7b7f50ee8a17429381fff7366536a55d +# f9b7187cdf73434f8f3033256f82c8b0 msgid "**MathML**" msgstr "" #: ../../src/tutorial.txt:875 -# 9d6c456da3c142899b750d3f6f9f8233 +# 8dc85b3552904d33b475538aaa6d4153 msgid "**Pyglet**" msgstr "" #: ../../src/tutorial.txt:881 -# 6e0df9ca7db140669f64040f43c0282f +# b70b6fac5c2a4d7a939b4be502201f3a msgid "If pyglet is installed, a pyglet window will open containing the LaTeX rendered expression:" msgstr "" #: ../../src/tutorial.txt:887 -# 22166c8dedc04edbb0a04c74ff3aa378 +# f8a7f4109a6c46318ae89b2cbaa56e16 msgid "Notes" msgstr "" #: ../../src/tutorial.txt:889 -# 797d8c95c8694e1bbeb93f41d1fbc1d5 +# 3efcd7fa91b749148d3d37fa4761e2cc msgid "``isympy`` calls ``pprint`` automatically, so that's why you see pretty printing by default." msgstr "" #: ../../src/tutorial.txt:892 -# c3d5232e2261441a9fd1872dc8b7a50f +# c30de4cd602145e48e54d06851d4de4d msgid "Note that there is also a printing module available, ``sympy.printing``. Other printing methods available through this module are:" msgstr "" #: ../../src/tutorial.txt:895 -# 24ffa7f0229c4cdd948390348cc328ba +# 66c0aafbfb39429db4b9ce7c22851803 msgid "``pretty(expr)``, ``pretty_print(expr)``, ``pprint(expr)``: Return or print, respectively, a pretty representation of ``expr``. This is the same as the second level of representation described above." msgstr "" #: ../../src/tutorial.txt:897 -# 987a02b34b68467fa83e0b355eea2bdc +# d07f7e29d1ed4f4c888519b2875cf691 msgid "``latex(expr)``, ``print_latex(expr)``: Return or print, respectively, a `LaTeX `_ representation of ``expr``" msgstr "" #: ../../src/tutorial.txt:899 -# d5a3718a7c2341ce91219f5801fd74d8 +# 91d84d2b6e8a49fc8f4de6df309e7138 msgid "``mathml(expr)``, ``print_mathml(expr)``: Return or print, respectively, a `MathML `_ representation of ``expr``." msgstr "" #: ../../src/tutorial.txt:901 -# b9da6ceba6d54d4697046c99d486b4c3 +# 7ae735209d6f4ba085acf12bf2a615ff msgid "``print_gtk(expr)``: Print ``expr`` to `Gtkmathview `_, a GTK widget that displays MathML code. The `Gtkmathview `_ program is required." msgstr "" #: ../../src/tutorial.txt:904 -# 906322664775464ca77ddbb16b2f49ee +# 5b9cd01ade724b2a9912a9bdf14e5f3c msgid "Further documentation" msgstr "" #: ../../src/tutorial.txt:906 -# ddfcc15b499c461ebd88c612379e432c +# 4a80b982956e4d2cb8daa8cd892b851b msgid "Now it's time to learn more about SymPy. Go through the :ref:`SymPy User's Guide ` and :ref:`SymPy Modules Reference `." msgstr "" #: ../../src/tutorial.txt:910 -# 6fc4675cc2ca4895a21b59e3cdfa2d15 +# dd842009c80e472c925015cdbb43abf5 msgid "Be sure to also browse our public `wiki.sympy.org `_, that contains a lot of useful examples, tutorials, cookbooks that we and our users contributed, and feel free to edit it." msgstr "" #: ../../src/tutorial.txt:915 -# 6c9599bc084b41a580831d5c3c597f64 +# 6f81344d40194b1e9626585ad7d61071 msgid "Translations" msgstr "" #: ../../src/tutorial.txt:917 -# 806c97ff7b32477a876bd5374eb7f773 +# d63477b361e444e5ba1312563eb9f3d3 msgid "This tutorial is also available in other languages:" msgstr "" diff --git a/doc/src/tutorial.ru.po b/doc/src/tutorial.ru.po index bb2d8f1c3742..83125a67c322 100644 --- a/doc/src/tutorial.ru.po +++ b/doc/src/tutorial.ru.po @@ -1,485 +1,745 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) 2008, 2009, 2010, 2011 SymPy Development Team # This file is distributed under the same license as the SymPy package. -# FIRST AUTHOR , YEAR. +# Alexey Subach , 2011 # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: SymPy 0.7.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-12-16 13:07\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" +"POT-Creation-Date: 2012-03-19 21:43\n" +"PO-Revision-Date: 2012-03-19 21:40+0400\n" +"Last-Translator: Alexey Gudchenko \n" "Language-Team: LANGUAGE \n" +"Language: Russian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +# 3b8d0d99cbd0485d8a0f874d50c2e0ba #: ../../src/tutorial.txt:5 -# f84d341a1db74f57ba29a7b6c3c2edd9 msgid "Tutorial" msgstr "Краткое руководство" +# 261dbc2a93064851bb80b80a8985f0bd #: ../../src/tutorial.txt:10 -# dae967c0fdb14ea88ac2acf861130402 msgid "Introduction" msgstr "Введение" +# d007423f3c5d4407b0ca3221168f4a1e #: ../../src/tutorial.txt:12 -# 946c4116f6b4470c9bd363baa4085f07 -msgid "SymPy is a Python library for symbolic mathematics. It aims to become a full-featured computer algebra system (CAS) while keeping the code as simple as possible in order to be comprehensible and easily extensible. SymPy is written entirely in Python and does not require any external libraries." +msgid "" +"SymPy is a Python library for symbolic mathematics. It aims to become a full-" +"featured computer algebra system (CAS) while keeping the code as simple as " +"possible in order to be comprehensible and easily extensible. SymPy is " +"written entirely in Python and does not require any external libraries." msgstr "" +"SymPy представляет собой открытую библиотеку символьных вычислений на языке " +"Python. Цель SymPy - стать полнофункциональной системой компьютерной алгебры " +"(CAS), при этом сохраняя код максимально понятным и легко расширяемым. SymPy " +"полностью написан на Python и не требует сторонних библиотек." +# 344f5db4e74749c1a76459f6cec09b63 #: ../../src/tutorial.txt:17 -# 7d5e63b794a44e069a63a12902c8c233 -msgid "This tutorial gives an overview and introduction to SymPy. Read this to have an idea what SymPy can do for you (and how) and if you want to know more, read the :ref:`SymPy User's Guide `, :ref:`SymPy Modules Reference `. or the `sources `_ directly." -msgstr "" - +msgid "" +"This tutorial gives an overview and introduction to SymPy. Read this to have " +"an idea what SymPy can do for you (and how) and if you want to know more, " +"read the :ref:`SymPy User's Guide `, :ref:`SymPy Modules Reference " +"`. or the `sources `_ directly." +msgstr "" +"Данное руководство представляет из себя введение в SymPy. В " +"нем вы узнаете об основных возможностях SymPy и каким образом использовать " +"эту программу. Если же вы желаете прочитать более подробное руководство, то " +"обратитесь к :ref:`Руководству пользователя SymPy `, :ref:`Описанию " +"модулей SymPy `. Можно также обратиться и к `исходному коду " +"`_ библиотеки." + +# 12619172a6874cb4af84282e3e1bfc39 #: ../../src/tutorial.txt:26 -# 6b78e0f225174e669fa5ab7877cb6b7f msgid "First Steps with SymPy" msgstr "Первые шаги с SymPy" +# 6f67d8997f1a474288d6bd50f5ddbc4f #: ../../src/tutorial.txt:28 -# 09726dfba6634928bc673ade5d89e66b -msgid "The easiest way to download it is to go to http://code.google.com/p/sympy/ and download the latest tarball from the Featured Downloads:" +msgid "" +"The easiest way to download it is to go to http://code.google.com/p/sympy/ " +"and download the latest tarball from the Featured Downloads:" msgstr "" +"Скачать SymPy проще всего с http://code.google.com/p/sympy/. В разделе " +"Featured, Downloads нужно найти и загрузить последнюю версию дистрибутива:" +# 65bde5e4aded4818bc11c4b9cfc8a3b7 #: ../../src/tutorial.txt:34 -# 95da13d57e534846abe6d3ae2b3f39d0 msgid "Unpack it:" msgstr "" +"Для Windows-систем, нужно скачать и запустить установочный .exe файл. В " +"POSIX-совместимых системах нужно скачать .tar.gz файл и распаковать его:" +# 76f6f7d72759408084134e093b5d9aea #: ../../src/tutorial.txt:40 -# e9b7c89586dc4262a1c0407754dcd7e9 msgid "and try it from a Python interpreter:" -msgstr "" +msgstr "и запустить из интерпретатора Python:" +# 4ddd39aeea714e9f8495cd77f7e4d7dd #: ../../src/tutorial.txt:54 -# f6ee1f1e72cd4de781fc5774120042fb -msgid "You can use SymPy as shown above and this is indeed the recommended way if you use it in your program. You can also install it using ``./setup.py install`` as any other Python module, or just install a package in your favourite Linux distribution, e.g.:" -msgstr "" - +msgid "" +"You can use SymPy as shown above and this is indeed the recommended way if " +"you use it in your program. You can also install it using ``./setup.py " +"install`` as any other Python module, or just install a package in your " +"favourite Linux distribution, e.g.:" +msgstr "" +"Если вы собираетесь использовать SymPy в вашей программе как модуль, " +"рекомендуется обращаться к нему таким же способом, как показано выше. " +"Установить модуль в систему можно или из скаченных исходников, используя " +"команду ./setup.py install. Или, если вы работаете в Linux, можно установить " +"пакет ``python-sympy`` с помощью системы установки программ:" + +# ce08a431018449c093786fe229ef9736 #: ../../src/tutorial.txt:80 -# cfcc2605ea584cf2af47c450edadccbf -msgid "For other means how to install SymPy, consult the Downloads_ tab on the SymPy's webpage." +msgid "" +"For other means how to install SymPy, consult the Downloads_ tab on the " +"SymPy's webpage." msgstr "" +"Другие способы установки SymPy описаны в разделе Downloads_ на домашней " +"странице SymPy." +# e81794f1d22c4f47b1f0b5f484ccb789 #: ../../src/tutorial.txt:87 -# a19913e1d97f4b2b8559d097d92e1851 msgid "isympy Console" -msgstr "" +msgstr "Консоль isympy" +# 342865b9196045a6a2a11ae9d69bc980 #: ../../src/tutorial.txt:89 -# ff2cbec326eb4f1f98083e84d4dec8db -msgid "For experimenting with new features, or when figuring out how to do things, you can use our special wrapper around IPython called ``isympy`` (located in ``bin/isympy`` if you are running from the source directory) which is just a standard python shell that has already imported the relevant sympy modules and defined the symbols x, y, z and some other things:" -msgstr "" - +msgid "" +"For experimenting with new features, or when figuring out how to do things, " +"you can use our special wrapper around IPython called ``isympy`` (located in " +"``bin/isympy`` if you are running from the source directory) which is just a " +"standard Python shell that has already imported the relevant SymPy modules " +"and defined the symbols x, y, z and some other things:" +msgstr "" +"SymPy можно использовать не только как модуль, но и как отдельную программу " +"``isympy``, которая расположена в папке ``bin`` относительно директории с " +"исходным кодом. Программа удобна для экспериментов с новыми функциями или " +"для обучения SymPy. Она использует стандартный терминал IPython, но с уже " +"включенными в нее важными модулями SymPy и определенными переменными x, y, z:" + +# 9d9f7e2ea55549268218f99a5b14593a #: ../../src/tutorial.txt:119 -# 4756c54ede09438e885a543ffe5c3c19 -msgid "Commands entered by you are bold. Thus what we did in 3 lines in a regular Python interpreter can be done in 1 line in isympy." +msgid "" +"Commands entered by you are bold. Thus what we did in 3 lines in a regular " +"Python interpreter can be done in 1 line in isympy." msgstr "" +"Команды введенные вами, обозначены жирным шрифтом. То, что мы делали тремя " +"строчками в стандартном терминале Python, мы можем сделать одной строчкой в " +"isympy. Кроме того программа поддерживает различные способы отображения " +"результатов, в том числе графические." +# f53c41b84d8b4d47894877572350325c #: ../../src/tutorial.txt:124 -# a50006bf0aed4e2a8a3967edb9ef7afa msgid "Using SymPy as a calculator" -msgstr "" +msgstr "Использование SymPy в качестве калькулятора" -#: ../../src/tutorial.txt:126 +# c6a7837a22d44c27a5d35a905779bd84 # 88460153b0404c1c80d7d9fef1b9d530 +#: ../../src/tutorial.txt:126 msgid "SymPy has three built-in numeric types: Float, Rational and Integer." msgstr "" +"SymPy поддерживает три типа численных данных: Float, Rational и Integer." +# 68eba98bb6f742d799aba6051f7f3d0f #: ../../src/tutorial.txt:128 -# 668a48de987a4bd6bb25e6319aa18ba2 -msgid "The Rational class represents a rational number as a pair of two Integers: the numerator and the denominator. So Rational(1,2) represents 1/2, Rational(5,2) represents 5/2, and so on." -msgstr "" - -#: ../../src/tutorial.txt:146 -# e2a928259dea44eaa3cabe0fee4bdd78 -msgid "Proceed with caution while working with Python int's and floating point numbers, especially in division, since you may create a Python number, not a SymPy number. A ratio of two Python ints may create a float -- the \"true division\" standard of Python 3 and the default behavior of ``isympy`` which imports division from __future__::" -msgstr "" - -#: ../../src/tutorial.txt:156 -# 3f86f4cd341e4872b08b2ae231c0c8c8 -msgid "But in earlier Python versions where division has not been imported, a truncated int will result::" +msgid "" +"The Rational class represents a rational number as a pair of two Integers: " +"the numerator and the denominator. So Rational(1,2) represents 1/2, Rational" +"(5,2) represents 5/2, and so on." msgstr "" +"Rational представляет собой обыкновенную дробь, которая задается с помощью " +"двух целых чисел: числителя и знаменателя. Например, Rational(1,2) " +"представляет дробь 1/2, Rational(5,2) представляет дробь 5/2, и так далее." -#: ../../src/tutorial.txt:162 -# 9dbc717a5cd64ee7977c80343f7a92d9 -msgid "In both cases, however, you are not dealing with a SymPy Number because Python created its own number. Most of the time you will probably be working with Rational numbers, so make sure to use Rational to get the SymPy result. One might find it convenient to equate ``R`` and Rational::" +# 4178f4aea2b54612a2a0c8f74fe2aa63 +#: ../../src/tutorial.txt:147 +msgid "" +"Proceed with caution while working with Python int's and floating point " +"numbers, especially in division, since you may create a Python number, not a " +"SymPy number. A ratio of two Python ints may create a float -- the \"true " +"division\" standard of Python 3 and the default behavior of ``isympy`` which " +"imports division from __future__::" +msgstr "" +"Важная особенность Python-интерпретатора, о которой нужно сказать отдельно, " +"связана с делением целых чисел При делении двух питоновских чисел типа int с " +"помощью оператора \"/\" в старых версиях Python в результате получается " +"число питоновского типа int. В Python 3 этот стандарт изменен на \"true " +"division\", и в результате получается питоновский тип float.И этот же " +"стандарт \"true division\" по умолчанию включен и в isympy::" + +# 723ba484cf134b71aaf8e7140c9a3954 +#: ../../src/tutorial.txt:157 +msgid "" +"But in earlier Python versions where division has not been imported, a " +"truncated int will result::" msgstr "" +"В более ранних версиях Python этого не получится, и результатом будет " +"целочисленное деление::" -#: ../../src/tutorial.txt:174 -# 3ee8ed81cd7e4bfdadf807a0b9b12e2d -msgid "We also have some special constants, like e and pi, that are treated as symbols (1+pi won't evaluate to something numeric, rather it will remain as 1+pi), and have arbitrary precision::" +# 0f88584459994baa87824d3fd4cf34f1 +#: ../../src/tutorial.txt:163 +msgid "" +"In both cases, however, you are not dealing with a SymPy Number because " +"Python created its own number. Most of the time you will probably be working " +"with Rational numbers, so make sure to use Rational to get the SymPy result. " +"One might find it convenient to equate ``R`` and Rational::" +msgstr "" +"Обратите внимание, что и в том и в другом случае вы имеете дело не с " +"объектом Number из библиотеки SymPy, который представляет число в SymPy, а с " +"питоновскими числами, которые создаются самим интерпретатором Python. Скорее " +"всего, вам нужно будете работать с дробными числами из библиотеки SymPy, " +"поэтому для того чтобы получать результат в виде объектов SymPy убедитесь, " +"что вы используете класс Rational. Кому-то может показаться удобным " +"обозначать Rational как ``R``::" + +# bf581140e5ea4f8f98031862c785375e +#: ../../src/tutorial.txt:175 +msgid "" +"We also have some special constants, like e and pi, that are treated as " +"symbols (1+pi won't evaluate to something numeric, rather it will remain as " +"1+pi), and have arbitrary precision::" msgstr "" +"В модуле Sympy имеются особые константы, такие как e и pi, которые ведут " +"себя как переменные (то есть выражение 1+pi не преобразуется сразу в число, " +"а так и останется 1+pi)::" -#: ../../src/tutorial.txt:187 -# a1a7546a35f24edd8c11b1f05ea460e2 +# 366cb3315e5f4371927639cebbf8a726 +#: ../../src/tutorial.txt:189 msgid "as you see, evalf evaluates the expression to a floating-point number" msgstr "" +"как вы видите, функция evalf переводит исходное выражение в число с " +"плавающей точкой. Вычисления можно проводить с большей точностью. Для этого " +"нужно передать в качестве аргумента этой функции требуемое число десятичных " +"знаков." -#: ../../src/tutorial.txt:189 -# fac72aa95a124064a2e37da6e411c43a +# b300cf55ed11465bac6e0991d6f61a15 +#: ../../src/tutorial.txt:191 msgid "The symbol ``oo`` is used for a class defining mathematical infinity::" msgstr "" +"Для работы с математической бесконечностью используется символ ``oo``::" -#: ../../src/tutorial.txt:197 -# aa2936b52b8042dead3f4bb1c049edc1 +# addc7fddfd294a0e8da003463f35b614 +#: ../../src/tutorial.txt:200 msgid "Symbols" +msgstr "Переменные" + +# 8508a3480ca34bd4a8741733e17ccc56 +#: ../../src/tutorial.txt:202 +msgid "" +"In contrast to other Computer Algebra Systems, in SymPy you have to declare " +"symbolic variables explicitly::" +msgstr "" +"В отличие от многих других систем компьютерной алгебры, вам нужно явно " +"декларировать символьные переменные::" + +# 19bcc88285724ae99b448a86d7980ce9 +#: ../../src/tutorial.txt:209 +msgid "" +"On the left is the normal Python variable which has been assigned to the " +"SymPy Symbol class. Predefined symbols (including those for symbols with " +"Greek names) are available for import from abc:" msgstr "" +"В левой части этого выражения находится переменная Python, которая " +"питоновским присваиванием соотносится с объектом класса Symbol из SymPy." -#: ../../src/tutorial.txt:199 -# a2cdfc56d552408aacd4e540068fba20 -msgid "In contrast to other Computer Algebra Systems, in SymPy you have to declare symbolic variables explicitly::" +# 6b654b5bda774aa0a4b150388bc102dc +#: ../../src/tutorial.txt:215 +msgid "" +"Symbols can also be created with the ``symbols`` or ``var`` functions, the " +"latter automatically adding the created symbols to the namespace, and both " +"accepting a range notation:" msgstr "" +"Символьные переменные могут также задаваться и с помощью функций ``symbols`` " +"или ``var``. Они допускают указание диапазона. Их отличие состоит в том, что " +"``var`` добавляет созданные переменные в текущее пространство имен:" -#: ../../src/tutorial.txt:207 -# 18ad6f7b7485464590ced492159ac132 -msgid "On the left is the normal Python variable which has been assigned to the SymPy Symbol class. Instances of the Symbol class \"play well together\" and are the building blocks of expresions::" +# aa93d9743638443ebbae8bc5bee19e56 +#: ../../src/tutorial.txt:227 +msgid "" +"Instances of the Symbol class \"play well together\" and are the building " +"blocks of expresions::" msgstr "" +"Экземпляры класса Symbol взаимодействуют друг с другом. Таким образом, с " +"помощью них конструируются алгебраические выражения::" -#: ../../src/tutorial.txt:220 -# 76d8e041db1c4be7926bb34608f07db0 -msgid "They can be substituted with other numbers, symbols or expressions using ``subs(old, new)``::" +# c613786cee894688b20cdccfeaac8c9b +#: ../../src/tutorial.txt:239 +msgid "" +"They can be substituted with other numbers, symbols or expressions using " +"``subs(old, new)``::" msgstr "" +"Переменные могут быть заменены на другие переменные, числа или выражения с " +"помощью функции подстановки ``subs(old, new)``::" -#: ../../src/tutorial.txt:231 -# 25a498aa5bce46298b6bf5909ae5c45e +# 3872add29a8b4a71a5e26a3218130491 +#: ../../src/tutorial.txt:250 msgid "For the remainder of the tutorial, we assume that we have run::" msgstr "" +"Теперь, с этого момента, для всех написанных ниже примеров мы будем " +"предполагать, что запустили следующую команду по настройке системы " +"отображения результатов::" -#: ../../src/tutorial.txt:235 -# 94b13605b8b74a32a7780b10b7c94cfa -msgid "This will make things look better when printed. See the :ref:`printing-tutorial` section below. If you have a unicode font installed, you can pass use_unicode=True for a slightly nicer output." +# d1b280862f04408a98ba048cc9460eaa +#: ../../src/tutorial.txt:255 +msgid "" +"This will make things look better when printed. See the :ref:`printing-" +"tutorial` section below. If you have a unicode font installed, you can pass " +"use_unicode=True for a slightly nicer output." msgstr "" +"Она придаст более качественное отображение выражений. Подробнее по системе " +"отображения и печати написано в разделе :ref:`printing-tutorial-ru`. Если же " +"у вас установлен шрифт с юникодом, вы можете использовать опцию " +"use_unicode=True для еще более красивого вывода." -#: ../../src/tutorial.txt:240 -# 292336f5375a4825bbf4806826fa3f02 +# 17ee30ecb05a410d94cccf0cc229ae5b +#: ../../src/tutorial.txt:260 msgid "Algebra" -msgstr "" +msgstr "Алгебра" -#: ../../src/tutorial.txt:242 -# 5c2f240042984f8cb50782f5d167085a +# 673fbe9f608748128027755ac1b0d7cb +#: ../../src/tutorial.txt:262 msgid "For partial fraction decomposition, use ``apart(expr, x)``::" msgstr "" +"Чтобы разложить выражение на простейшие дроби используется функция ``apart" +"(expr, x)``::" -#: ../../src/tutorial.txt:264 -# fa0a6c4d37934cb582f9f54330f04797 +# 723c57a6e36f43eca18771e2ad246955 +#: ../../src/tutorial.txt:287 msgid "To combine things back together, use ``together(expr, x)``::" msgstr "" +"Чтобы снова привести дробь к общему знаменателю используется функция " +"``together(expr, x)``::" -#: ../../src/tutorial.txt:285 -# c50715a5f4c24b51a02423023e843c4a +# 607fa824a12d461d9eaf6c83bbc902d0 +#: ../../src/tutorial.txt:309 msgid "Calculus" -msgstr "" +msgstr "Вычисления" -#: ../../src/tutorial.txt:290 -# bbf0449787ed48dcaceb6997bd1af1d9 +# 9cbc010ae5b54d2a9222db8a78a2687c +#: ../../src/tutorial.txt:314 msgid "Limits" -msgstr "" +msgstr "Пределы" -#: ../../src/tutorial.txt:292 -# 30fd6e8326d146e1a90ecf4a9d974039 -msgid "Limits are easy to use in sympy, they follow the syntax ``limit(function, variable, point)``, so to compute the limit of f(x) as x -> 0, you would issue ``limit(f, x, 0)``::" +# e2cfd8cf367b4f41a2eb8f98045e1f45 +#: ../../src/tutorial.txt:316 +msgid "" +"Limits are easy to use in SymPy, they follow the syntax ``limit(function, " +"variable, point)``, so to compute the limit of f(x) as x -> 0, you would " +"issue ``limit(f, x, 0)``::" msgstr "" +"Пределы считаются в SymPy очень легко. Чтобы вычислить предел функции, " +"используйте функцию ``limit(function, variable, point)``. Например, чтобы " +"вычислить предел f(x) при x -> 0, вам нужно ввести ``limit(f, x, 0)``::" -#: ../../src/tutorial.txt:301 -# b61572954db04b9bbadb7b3214bce620 +# 7cc96e845d0a4c479199f4a51ed7d665 +#: ../../src/tutorial.txt:325 msgid "you can also calculate the limit at infinity::" -msgstr "" +msgstr "также вы можете вычислять пределы при x, стремящемся к бесконечности::" -#: ../../src/tutorial.txt:312 -# 3b225244a3534797943e1911bc99109c -msgid "for some non-trivial examples on limits, you can read the test file `test_demidovich.py `_" +# d67693fb8cc04954be3bff6aaa712bfa +#: ../../src/tutorial.txt:336 +msgid "" +"for some non-trivial examples on limits, you can read the test file " +"`test_demidovich.py `_" msgstr "" +"Для более сложных примеров вычисления пределов, вы можете обратится к файлу " +"с тестами `test_demidovich.py `_ " -#: ../../src/tutorial.txt:319 -# 839cdd6f6dc246d381cf4ae32fe75973 +# c92009d7b6df4e2d8f9fe9ad14657bbd +#: ../../src/tutorial.txt:343 msgid "Differentiation" -msgstr "" +msgstr "Дифференцирование" -#: ../../src/tutorial.txt:321 -# 8e81b82b9f6f44e1ab31a0eecf761366 -msgid "You can differentiate any SymPy expression using ``diff(func, var)``. Examples::" +# 476aa0220145495a932178c9fc3b305a +#: ../../src/tutorial.txt:345 +msgid "" +"You can differentiate any SymPy expression using ``diff(func, var)``. " +"Examples::" msgstr "" +"Продифференцировать любое выражение SymPy, можно используя ``diff(func, var)" +"``. Примеры::" -#: ../../src/tutorial.txt:334 -# 351f72597cd9412b86c05ec2d2ad9dc7 +# 35850640eccf4071b1b0318a49055f39 +#: ../../src/tutorial.txt:358 msgid "You can check, that it is correct by::" msgstr "" +"Можно, кстати, через пределы проверить правильность вычислений производной::" -#: ../../src/tutorial.txt:340 -# cae093b9d4874e55af4aa3e86c9333a8 -msgid "Higher derivatives can be calculated using the ``diff(func, var, n)`` method::" +# 5ffab586313e4970aa3c933d631bc676 +#: ../../src/tutorial.txt:366 +msgid "" +"Higher derivatives can be calculated using the ``diff(func, var, n)`` " +"method::" msgstr "" +"Производные более высших порядков можно вычислить, используя дополнительный " +"параметр этой же функции ``diff(func, var, n)``::" -#: ../../src/tutorial.txt:357 -# 6798e5997ac849c5bfff0a0f32eb45a0 +# 0624a0af400f4e78a351b056a7ba009c +#: ../../src/tutorial.txt:383 msgid "Series expansion" -msgstr "" +msgstr "Разложение в ряд" -#: ../../src/tutorial.txt:359 -# 6be2148c83b94a408dddc9fb92a20464 +# b40c746b225d4749a9298ad077c3ce4d +#: ../../src/tutorial.txt:385 msgid "Use ``.series(var, point, order)``::" msgstr "" +"Для разложения в ряд используйте метод ``.series(var, point, order)``::" -#: ../../src/tutorial.txt:374 -# a563c2fe1ff84fee9b610aebf5c12392 +# f9106b494ab2465bbbd4108736f67a1e +#: ../../src/tutorial.txt:400 msgid "Another simple example::" -msgstr "" - -#: ../../src/tutorial.txt:387 -# ecca6ec75765476d85cee470068c9392 -msgid "That should print the following after the execution::" -msgstr "" +msgstr "Еще один простой пример::" -#: ../../src/tutorial.txt:399 -# 8af7fecfb1ca4c4993be2cce5b8f71cc +# d4aa5a4ef31d4327a70c47b42cc0d2d4 +#: ../../src/tutorial.txt:420 msgid "Integration" -msgstr "" +msgstr "Интегрирование" -#: ../../src/tutorial.txt:401 -# f81cf544a82f45fd9692fd3396982404 -msgid "SymPy has support for indefinite and definite integration of transcendental elementary and special functions via ``integrate()`` facility, which uses powerful extended Risch-Norman algorithm and some heuristics and pattern matching::" +# 8e985ebba8d5463bb1af3a3220dd21b0 +#: ../../src/tutorial.txt:422 +msgid "" +"SymPy has support for indefinite and definite integration of transcendental " +"elementary and special functions via ``integrate()`` facility, which uses " +"powerful extended Risch-Norman algorithm and some heuristics and pattern " +"matching::" msgstr "" +"SymPy поддерживает вычисление определенных и неопределенных интегралов с " +"помощью функции ``integrate()``. Она использует расширенный алгоритм Риша-" +"Нормана и некоторые шаблоны и эвристики. Можно вычислять интегралы " +"трансцендентных, простых и специальных функций::" -#: ../../src/tutorial.txt:409 -# 3a1e02d1dd1c47afbb378ed9627a9a6e +# 6ff3ff4c189f45078f75a93aa2863ef9 +#: ../../src/tutorial.txt:430 msgid "You can integrate elementary functions::" -msgstr "" +msgstr "Вы можете интегрировать простейшие функции::" -#: ../../src/tutorial.txt:422 -# 5393cd6b23a1412897e45ad8728e211c +# ead91a6c777f47dea8be1a1e50bdf752 +#: ../../src/tutorial.txt:443 msgid "Also special functions are handled easily::" -msgstr "" +msgstr "Примеры интегрирования некоторых специальных функций::" -#: ../../src/tutorial.txt:430 -# 6be3be4378614e3883fafdcf154a07e1 -msgid "It is possible to compute definite integral::" -msgstr "" +# 40ad8cd5c7e3460f9cceb59630cf562c +#: ../../src/tutorial.txt:451 +msgid "It is possible to compute definite integrals::" +msgstr "Возможно также вычислить определенный интеграл::" -#: ../../src/tutorial.txt:439 -# 06c6ca9048d147f5a7b35ade18b3eb06 -msgid "Also improper integrals are supported as well::" -msgstr "" +# 19602e9ed1b641f993f704eb31111beb +#: ../../src/tutorial.txt:460 +msgid "Also, improper integrals are supported as well::" +msgstr "Поддерживаются и несобственные интегралы::" -#: ../../src/tutorial.txt:451 -# 06e8b87c46524596870768e0c1b70375 +# b8ef574746f1448ab78da1958f9de616 +#: ../../src/tutorial.txt:472 msgid "Complex numbers" +msgstr "Комплексные числа" + +# 13fbbe2555b14aefb25e48ac5ed298a0 +#: ../../src/tutorial.txt:474 +msgid "" +"Besides the imaginary unit, I, which is imaginary, symbols can be created " +"with attributes (e.g. real, positive, complex, etc...) and this will affect " +"how they behave::" msgstr "" +"Помимо мнимой единицы I, которое является мнимым числом, символы тоже могут " +"иметь специальные атрибуты (real, positive, complex и т.д), которые " +"определяют поведение этих символов при вычислении символьных выражений::" -#: ../../src/tutorial.txt:468 -# abb5f8441604412088acea01599d35ca +# d726887f4f0444dba6e4ef6a2bb702d6 +#: ../../src/tutorial.txt:491 msgid "Functions" -msgstr "" +msgstr "Функции" -#: ../../src/tutorial.txt:470 -# 45e5a176a38a4d6a817ba27ecbe21fb2 +# 42585fb828e24af79c0278aa14bcc2c3 +#: ../../src/tutorial.txt:493 msgid "**trigonometric**::" -msgstr "" +msgstr "**тригонометрические**::" -#: ../../src/tutorial.txt:516 -# fbfc937ac98f4b24976c97a9d186d8df +# be0be9ba76c042b68160f1907d2b0d13 +#: ../../src/tutorial.txt:542 msgid "**spherical harmonics**::" -msgstr "" +msgstr "**сферические**::" -#: ../../src/tutorial.txt:541 -# 589cd74e37ba4bd999e3ad52111ccae0 +# bdc2ad96783444459c3e66c409914143 +#: ../../src/tutorial.txt:568 msgid "**factorials and gamma function**::" -msgstr "" +msgstr "**факториалы и гамма-функции**::" -#: ../../src/tutorial.txt:559 -# 53393f998d7d42148aca75cea7613c2e +# c621f51005504ee9890d34156556de57 +#: ../../src/tutorial.txt:586 msgid "**zeta function**::" -msgstr "" +msgstr "**дзета-функции**::" -#: ../../src/tutorial.txt:583 -# 6e1ba3c25e1c495b86c474aec7febb89 +# 95ad3014dc9442cc8eb8438b2cc28327 +#: ../../src/tutorial.txt:611 msgid "**polynomials**::" -msgstr "" +msgstr "**многочлены**::" -#: ../../src/tutorial.txt:621 -# 1f1c9e26c1964efc871080bbdcf7ce25 +# d91cad90caee4aa7b2922a42ebe608b1 +#: ../../src/tutorial.txt:650 msgid "Differential Equations" -msgstr "" +msgstr "Дифференциальные уравнения" -#: ../../src/tutorial.txt:623 -#: ../../src/tutorial.txt:640 -# 59b93c90e0674876927c83388979f2af -# cd9b7d592e2a4d7495e2fcdfca2396a2 +# 18467aefa0364cec936571494c1993da +# 65279c1f5c4a448fbb813790d956ac48 +#: ../../src/tutorial.txt:652 ../../src/tutorial.txt:672 msgid "In ``isympy``::" -msgstr "" +msgstr "В ``isympy``::" -#: ../../src/tutorial.txt:638 -# 4cffa88d2f5a41ffad3ca85263be5cdc +# e036530fce79489cbf1c494a9443568f +#: ../../src/tutorial.txt:670 msgid "Algebraic equations" -msgstr "" +msgstr "Алгебраические уравнения" -#: ../../src/tutorial.txt:651 -# 31729299063d4327ba7ca7be58cbe527 +# fd5aedc6ba9f45b4868c10764f9e6797 +#: ../../src/tutorial.txt:685 msgid "Linear Algebra" -msgstr "" +msgstr "Линейная алгебра" -#: ../../src/tutorial.txt:656 -# cdd55a83a58849d8b269cf4510cca6d1 +# 372cf49866874ec1a97d6cb813f8b63b +#: ../../src/tutorial.txt:690 msgid "Matrices" -msgstr "" +msgstr "Матрицы" -#: ../../src/tutorial.txt:658 -# 8257ad14575648b9a440a388e92e358a +# fc4620da22164a3c8f25be8591759f97 +#: ../../src/tutorial.txt:692 msgid "Matrices are created as instances from the Matrix class::" -msgstr "" +msgstr "Матрицы задаются с помощью конструктора Matrix::" -#: ../../src/tutorial.txt:666 -# b1ba9d6bc4dd4f04b169a9dce0c0d99c -msgid "you can also put Symbols in it::" -msgstr "" +# 0289f9cec4f140c6adc90aa38f84efee +#: ../../src/tutorial.txt:700 +msgid "They can also contain symbols::" +msgstr "В матрицах вы также можете использовать символьные переменные::" -#: ../../src/tutorial.txt:681 -# bfa056d84c964bdc9ee54b572767409c -msgid "For more information an examples with Matrices, see the LinearAlgebraTutorial." +# ced23cc091a34db6be3b5597fd056f32 +#: ../../src/tutorial.txt:715 +msgid "For more about Matrices, see the Linear Algebra tutorial." msgstr "" +"Для того, чтобы узнать о матрицах подробнее, прочитайте, пожалуйста, " +"Руководство по Линейной Алгебре." -#: ../../src/tutorial.txt:686 -# ce462773103a4863b167e3e262088ae9 +# 71124cfe123649c28c7a61251d0b1cdd +#: ../../src/tutorial.txt:720 msgid "Pattern matching" -msgstr "" +msgstr "Сопоставление с образцом" -#: ../../src/tutorial.txt:688 -# a5d8f822d44c4096961c41b08cff6e14 -msgid "Use the ``.match()`` method, along with the ``Wild`` class, to perform pattern matching on expressions. The method will return a dictionary with the required substitutions, as follows::" +# b9ecf875555748978255bc268bc4123c +#: ../../src/tutorial.txt:722 +msgid "" +"Use the ``.match()`` method, along with the ``Wild`` class, to perform " +"pattern matching on expressions. The method will return a dictionary with " +"the required substitutions, as follows::" msgstr "" +"Чтобы сопоставить выражения с образцами, используйте функцию ``.match()`` " +"вместе со вспомогательным классом ``Wild``. Эта функция вернет словарь с " +"необходимыми заменами, например::" -#: ../../src/tutorial.txt:702 -# 5f158aedb06b423db47571b42707a90e +# 06d4a8e419854045af79b0076e71616a +#: ../../src/tutorial.txt:736 msgid "If the match is unsuccessful, it returns ``None``::" -msgstr "" +msgstr "Если же сопоставление не удалось, функция вернет``None``::" -#: ../../src/tutorial.txt:707 -# 94228299551d420ea0cf91f8fd4771b9 -msgid "One can also use the exclude parameter of the ``Wild`` class to ensure that certain things do not show up in the result::" +# 7219ffc6e8b0486a913297c19b5fce1f +#: ../../src/tutorial.txt:741 +msgid "" +"One can also use the exclude parameter of the ``Wild`` class to ensure that " +"certain things do not show up in the result::" msgstr "" +"Также можно использовать параметр exclude для исключения некоторых значений " +"из результата::" -#: ../../src/tutorial.txt:722 -# c37af88a913f435981453293ccbb69e5 +# a692f291013a4777a4771188901c6bf2 +#: ../../src/tutorial.txt:755 msgid "Printing" -msgstr "" +msgstr "Печать" -#: ../../src/tutorial.txt:724 -# 4a29361607f2475989e5138637b0073a -msgid "There are many ways how expressions can be printed." -msgstr "" +# 8cfe16423a79408c828d270def412899 +#: ../../src/tutorial.txt:757 +msgid "There are many ways to print expressions." +msgstr "Реализовано несколько способов вывода выражений на экран." -#: ../../src/tutorial.txt:726 -# bfb32e3c79d843a2b8cbad8e5e3eb4b8 +# a9891e6028b644879ce5e3a8489a72a5 +#: ../../src/tutorial.txt:759 msgid "**Standard**" -msgstr "" +msgstr "**Стандартный**" -#: ../../src/tutorial.txt:728 -# 56356973c075448783d26e47c6a06064 +# 9802b10f6c2d4b7997302f4315e8bf2a +#: ../../src/tutorial.txt:761 msgid "This is what ``str(expression)`` returns and it looks like this:" msgstr "" +"Стандартный способ представлен функцией ``str(expression)``, которая " +"работает следующим образом:" -#: ../../src/tutorial.txt:741 -# f1b2fcd3f3414b6e91cab1f71b6a4e1a +# 461df293b09c46b1bf24bc5c335b4401 +#: ../../src/tutorial.txt:772 msgid "**Pretty printing**" -msgstr "" +msgstr "**Красивая печать**" -#: ../../src/tutorial.txt:743 -# a0d1b3c10c144fa19f684a85fe622493 -msgid "This is a nice ascii-art printing produced by a ``pprint`` function:" +# 29c9e2204a0948db9d8328300f3bdd1a +#: ../../src/tutorial.txt:774 +msgid "Nice ascii-art printing is produced by the ``pprint`` function:" msgstr "" +"Этот способ печати выражений основан на ascii-графике и реализован через " +"функцию ``pprint``:" -#: ../../src/tutorial.txt:762 -# 9af477bd9adc4d09bf14defba6cdc2bf -msgid "If you have a unicode font installed, it should use unicode pretty printing by default. You can override this using the ``use_unicode`` option.:" +# 5fc9fa809fa04cee8113a669f032b9c3 +#: ../../src/tutorial.txt:793 +msgid "" +"If you have a unicode font installed, the ``pprint`` function will use it by " +"default. You can override this using the ``use_unicode`` option.:" msgstr "" +"Если у вас установлен шрифт с юникодом, он будет использовать Pretty-print с " +"юникодом по умолчанию. Эту настройку можно отключить, используя " +"``use_unicode``:" -#: ../../src/tutorial.txt:772 -# 84d8d1e95190474fb633112eaca07332 -msgid "See also the wiki `Pretty Printing `_ for more examples of a nice unicode printing." +# dcbf982351c1414bb0c2653039d6f569 +#: ../../src/tutorial.txt:803 +msgid "" +"See also the wiki `Pretty Printing `_ for more examples of a nice unicode printing." msgstr "" +"Для изучения подробных примеров работы Pretty-print с юникодом вы можете " +"обратится к статье `Pretty Printing `_ из нашего Вики." -#: ../../src/tutorial.txt:776 -# 919e23979e684267a33d1f30399957ed -msgid "Tip: To make the pretty printing default in the python interpreter, use::" +# af26680251b0437e807a48d7946c3724 +#: ../../src/tutorial.txt:807 +msgid "" +"Tip: To make pretty printing the default in the Python interpreter, use::" msgstr "" +"Совет: Чтобы активировать Pretty-print по умолчанию в интерпретаторе Python, " +"используйте::" -#: ../../src/tutorial.txt:799 -# 4e1d49f68b984ff3942a1ad1b4e98100 +# b34cf35aca67417bbedfd0f9912d3dab +#: ../../src/tutorial.txt:830 msgid "**Python printing**" -msgstr "" +msgstr "**Печать объектов Python**" -#: ../../src/tutorial.txt:816 -# f8a8af14ba474b2fad3f553da2b898b6 +# f6300e28499f4c23b1a18aca896340bb +#: ../../src/tutorial.txt:846 msgid "**LaTeX printing**" -msgstr "" +msgstr "**Печать в формате LaTeX**" -#: ../../src/tutorial.txt:833 -# ff5160027cfd45ad87915f270bf6b6cf +# 3b81c7e35c364676a8ec8587b515ac8b +#: ../../src/tutorial.txt:863 msgid "**MathML**" -msgstr "" +msgstr "**MathML**" -#: ../../src/tutorial.txt:845 -# 43fb7c8304f145748db346289d43e36a +# ee36b86c5d54419fbde01ac89cab213b +#: ../../src/tutorial.txt:875 msgid "**Pyglet**" -msgstr "" +msgstr "**Pyglet**" -#: ../../src/tutorial.txt:851 -# 64d1c80142fd4e29b7d0153b753423c1 -msgid "And a pyglet window with the LaTeX rendered expression will popup:" -msgstr "" +# 8ea61d943b2b4960a879932eba46bc1b +#: ../../src/tutorial.txt:881 +msgid "" +"If pyglet is installed, a pyglet window will open containing the LaTeX " +"rendered expression:" +msgstr "Появится окно pyglet с отрисованным выражением LaTeX:" -#: ../../src/tutorial.txt:856 -# fa6c57fbfbaf42be8755edea03f65b33 +# 8741a66d24b04738a563afc90a41567b +#: ../../src/tutorial.txt:887 msgid "Notes" -msgstr "" +msgstr "Примечания" -#: ../../src/tutorial.txt:858 -# abd59a7cd8f942e28c3a44694ab7a6e5 -msgid "``isympy`` calls ``pprint`` automatically, so that's why you see pretty printing by default." +# 51219a05b9a84e79b754d8d03b7fc4fb +#: ../../src/tutorial.txt:889 +msgid "" +"``isympy`` calls ``pprint`` automatically, so that's why you see pretty " +"printing by default." msgstr "" +"``isympy`` вызывает ``pprint`` автоматически, по этой причине Pretty-print " +"будет включен в ``isympy`` по умолчанию." -#: ../../src/tutorial.txt:861 -# a55e545ba25141af9f6bd2df4b35f02c -msgid "Note that there is also a printing module available, ``sympy.printing``. Other printing methods available through this module are:" +# 7b126ec3363b4c63a6d6b98728a9738a +#: ../../src/tutorial.txt:892 +msgid "" +"Note that there is also a printing module available, ``sympy.printing``. " +"Other printing methods available through this module are:" msgstr "" +"Также доступен модуль печати - ``sympy.printing``. Через этот модуль " +"доступны следующий функции печати:" -#: ../../src/tutorial.txt:864 -# e5329a0202044246a97cad451de550a0 -msgid "``pretty(expr)``, ``pretty_print(expr)``, ``pprint(expr)``: Return or print, respectively, a pretty representation of ``expr``. This is the same as the second level of representation described above." +# 1001063c4e97442dbcb0491eb0641bba +#: ../../src/tutorial.txt:895 +msgid "" +"``pretty(expr)``, ``pretty_print(expr)``, ``pprint(expr)``: Return or print, " +"respectively, a pretty representation of ``expr``. This is the same as the " +"second level of representation described above." msgstr "" +"``pretty(expr)``, ``pretty_print(expr)``, ``pprint(expr)``: Возвращает или " +"выводит на экран, соответственно, \"Красивое\" представление выражения " +"``expr``. " -#: ../../src/tutorial.txt:866 -# 0bac12dbda6540028b304f4b655ae8e3 -msgid "``latex(expr)``, ``print_latex(expr)``: Return or print, respectively, a `LaTeX `_ representation of ``expr``" +# ee0ca8e34ce04b458228cb9d6565ec1a +#: ../../src/tutorial.txt:897 +msgid "" +"``latex(expr)``, ``print_latex(expr)``: Return or print, respectively, a " +"`LaTeX `_ representation of ``expr``" msgstr "" +"``latex(expr)``, ``print_latex(expr)``: Возвращает или выводит на экран, " +"соответственно, `LaTeX `_ -представление " +"``expr``" -#: ../../src/tutorial.txt:868 -# 7ec29518a7074721824c874431d421b8 -msgid "``mathml(expr)``, ``print_mathml(expr)``: Return or print, respectively, a `MathML `_ representation of ``expr``." +# ebfeade491644b0b85f72b00bd3c1bdf +#: ../../src/tutorial.txt:899 +msgid "" +"``mathml(expr)``, ``print_mathml(expr)``: Return or print, respectively, a " +"`MathML `_ representation of ``expr``." msgstr "" +"``mathml(expr)``, ``print_mathml(expr)``: Возвращает или выводит на экран, " +"соответственно, `MathML `_ -представление ``expr``." -#: ../../src/tutorial.txt:870 -# db586a15ee234c81a4fc8c2142a7f669 -msgid "``print_gtk(expr)``: Print ``expr`` to `Gtkmathview `_, a GTK widget that displays MathML code. The `Gtkmathview `_ program is required." +# bc0334fae2224eb2bfc81a55e7f522dd +#: ../../src/tutorial.txt:901 +msgid "" +"``print_gtk(expr)``: Print ``expr`` to `Gtkmathview `_, a GTK widget that displays MathML code. The `Gtkmathview " +"`_ program is required." msgstr "" +"``print_gtk(expr)``: Вывод ``expr`` в `Gtkmathview `_, виджет GTK, который отображает код MathML. Необходимо " +"наличие пакета `Gtkmathview `_ ." -#: ../../src/tutorial.txt:873 -# 45d07b3cdda546fdbd118b309704bd24 +# e76c7af419504e458893b76b1b485fd4 +#: ../../src/tutorial.txt:904 msgid "Further documentation" -msgstr "" +msgstr "Другие справочники" -#: ../../src/tutorial.txt:875 -# 0d07680d11e74714a5138cdb1428893d -msgid "Now it's time to learn more about SymPy. Go through the :ref:`SymPy User's Guide ` and :ref:`SymPy Modules Reference `." -msgstr "" - -#: ../../src/tutorial.txt:879 -# 0870c87ad84e4bab908444a6e3552d15 -msgid "Be sure to also browse our public `wiki.sympy.org `_, that contains a lot of useful examples, tutorials, cookbooks that we and our users contributed and we encourage you to edit it." +# 2757e954e4c04b01bf9607f2127c3146 +#: ../../src/tutorial.txt:906 +msgid "" +"Now it's time to learn more about SymPy. Go through the :ref:`SymPy User's " +"Guide ` and :ref:`SymPy Modules Reference `." msgstr "" +"Чтобы узнать о SymPy подробнее, обратитесь :ref:`Руководство пользователя " +"SymPy ` и :ref:`Описание модулей SymPy `." +# a037027043bd483dab5a52bd8d325980 +#: ../../src/tutorial.txt:910 +msgid "" +"Be sure to also browse our public `wiki.sympy.org `_, that contains a lot of useful examples, tutorials, cookbooks that we " +"and our users contributed, and feel free to edit it." +msgstr "" +"Также можно обратится на `wiki.sympy.org `_ - сайт, " +"который содержит множество полезных примеров, руководств и советов. Они " +"созданны нами и нашим сообществом. Мы будем рады, если и вы внесете в него " +"свой весомый вклад." + +# f6e1f1f3b1214ffc848b88bd85bc8bfd +#: ../../src/tutorial.txt:915 +msgid "Translations" +msgstr "Переводы" + +# cf4fe2ed5e27427cb8d4af80f7e88c3f +#: ../../src/tutorial.txt:917 +msgid "This tutorial is also available in other languages:" +msgstr "Этот текст доступен на других языках:" diff --git a/doc/src/tutorial.ru.txt b/doc/src/tutorial.ru.txt deleted file mode 100644 index 63b8171c696b..000000000000 --- a/doc/src/tutorial.ru.txt +++ /dev/null @@ -1,921 +0,0 @@ -.. _tutorial-ru: - -=================== -Краткое руководство -=================== - -.. role:: input(strong) - -Введение -======== - -SymPy - это открытая библиотека для символьных вычислений на языке Python. -Ее цель - стать полнофункциональной системой компьютерной алгебры (CAS), -сохраняя код максимально простым, понятным и легко расширяемым. -SymPy полностью написан на Python и не требует сторонних библиотек. - -Это руководство представляет из себя обзор и введение в SymPy. -Прочитайте его, чтобы узнать, что и как может сделать SymPy, а если -вы хотите узнать больше, прочитайте -:ref:`Руководство пользователя SymPy `, -:ref:`Описание модулей SymPy `. -или сразу `исходный код -`_ . - -Первые шаги с SymPy -=================== - -Самый простой способ скачать SymPy - пройти на -http://code.google.com/p/sympy/ и -загрузить .tar файл с последней версией из блока Featured Downloads: - -.. image:: figures/featured-downloads.png - -Распаковать его: - -.. parsed-literal:: - - $ :input:`tar xzf sympy-0.5.12.tar.gz` - -и запустить из интерпретатора Python: - -.. parsed-literal:: - - $ :input:`cd sympy-0.5.12` - $ :input:`python` - Python 2.4.4 (#2, Jan 3 2008, 13:36:28) - [GCC 4.2.3 20071123 (prerelease) (Debian 4.2.2-4)] on linux2 - Type "help", "copyright", "credits" or "license" for more information. - >>> from sympy import Symbol, cos - >>> x = Symbol("x") - >>> (1/cos(x)).series(x, 0, 10) - 1 + x**2/2 + 5*x**4/24 + 61*x**6/720 + 277*x**8/8064 + O(x**10) - -Если вы используете SymPy в вашей программе, -рекомендуется использовать его как показано выше. -Вы можете установить SymPy так же как и любой другой Python модуль, -используя ./setup.py install. Или, если вы работаете в Linux, можно -установить пакет ``python-sympy`` с помощью системы установки программ: - -.. topic:: Установка SymPy в Debian - - .. parsed-literal:: - - $ :input:`sudo apt-get install python-sympy` - Reading package lists... Done - Building dependency tree - Reading state information... Done - The following NEW packages will be installed: - python-sympy - 0 upgraded, 1 newly installed, 0 to remove and 18 not upgraded. - Need to get 991kB of archives. - After this operation, 5976kB of additional disk space will be used. - Get:1 http://ftp.cz.debian.org unstable/main python-sympy 0.5.12-1 [991kB] - Fetched 991kB in 2s (361kB/s) - Selecting previously deselected package python-sympy. - (Reading database ... 232619 files and directories currently installed.) - Unpacking python-sympy (from .../python-sympy_0.5.12-1_all.deb) ... - Setting up python-sympy (0.5.12-1) ... - -Для других вариантов установки SymPy, посмотрите, пожалуйста, -вкладку Downloads_ на домашней странице SymPy. - -.. _Downloads: http://code.google.com/p/sympy/wiki/DownloadInstallation?tm=2 - - -Консоль isympy --------------- - -Для экспериментов с новыми функциями, или для обучения SymPy, вы можете -использовать специальную оболочку IPython под названием ``isympy`` -(она расположена в ``bin/isympy``, относительно директории с исходным кодом), -которая является стандартным терминалом Python, но с уже импортированными -важными модулями SymPy, определенными переменными x, y, z и некоторыми -другими надстройками: - -.. parsed-literal:: - - $ :input:`cd sympy` - $ :input:`./bin/isympy` - IPython console for SymPy 0.7.1-git (Python 2.7.1) (ground types: gmpy) - - These commands were executed: - >>> from __future__ import division - >>> from sympy import * - >>> x, y, z, t = symbols('x y z t') - >>> k, m, n = symbols('k m n', integer=True) - >>> f, g, h = symbols('f g h', cls=Function) - - Documentation can be found at http://www.sympy.org - - In [1]: :input:`(1/cos(x)).series(x, 0, 10)` - Out[1]: - 2 4 6 8 - x 5*x 61*x 277*x / 10\ - 1 + ── + ──── + ───── + ────── + O\x / - 2 24 720 8064 - -.. note:: - - Команды, введенные вами, обозначены жирным шрифтом. То, что мы делали - тремя строчками в - стандартном терминале Python, мы можем сделать одной строчкой в isympy. - - -Использование SymPy в качестве калькулятора -------------------------------------------- - -SymPy поддерживает три типа численных данных: Float, Rational и Integer. - -Rational реализует обыкновенную дробь с помощью двух целых чисел: -числителя и знаменателя. Так, Rational(1,2) является числом 1/2, -Rational(5,2) является 5/2, и так далее. - -:: - - >>> from sympy import Rational - >>> a = Rational(1,2) - - >>> a - 1/2 - - >>> a*2 - 1 - - >>> Rational(2)**50/Rational(10)**50 - 1/88817841970012523233890533447265625 - - -В Python-интерпретаторе при делении двух питоновских чисел типа int -с помощью оператора "/" получается или питоновское int -(в старых версиях, без использования ``from __future__ import division``) -или питоновский тип float (стандарт "true division" в Python 3). -Этот же стандарт "true division" по умолчанию включен и в isympy:: - - >>> 1/2 #doctest: +SKIP - 0.5 - -В более ранних версиях Python этого не произойдет, -и результатом будет целочисленное деление:: - - >>> 1/2 #doctest: +SKIP - 0 - -В обоих случаях вы имеете дело не с объектом Number, который -представляет число в SymPy, а с питоновскими числами, которые создаются -по умолчанию. Скорее всего, в большинстве случаев вы будете работать -с дробными числами, поэтому для того чтобы получать результат в виде -объектов SymPy убедитесь, что вы используете класс Rational. -Кому-то может показаться удобным обозначать Rational как ``R``:: - - >>> R = Rational - >>> R(1, 2) - 1/2 - >>> R(1)/2 # R(1) - это Integer sympy, а результат деления Integer на int - Rational - 1/2 - -Также в модуле SymPy зарезервированы особые константы, такие как e и pi, -которые ведут себя как переменные (то есть 1+pi не сконвертируется в число, -а останется выражением 1+pi), и заданы с некоторой точностью:: - - >>> from sympy import pi, E - >>> pi**2 - pi**2 - - >>> pi.evalf() - 3.14159265358979 - - >>> (pi + E).evalf() - 5.85987448204884 - -как вы видите, функция evalf переводит выражение в число с плавающей точкой - -Символ ``oo`` используется для работы с математической бесконечностью:: - - >>> from sympy import oo - >>> oo > 99999 - True - >>> oo + 1 - oo - -Переменные ----------- - -В отличие от многих других систем компьютерной алгебры, в SymPy вам нужно явно -декларировать символьные переменные:: - - >>> from sympy import Symbol - >>> x = Symbol('x') - >>> y = Symbol('y') - -В левой части выражения присваивания записывается переменная Python, -которая соотносится с объектом класса Symbol в SymPy. -Экземпляры класса Symbol взаимодействуют друг с другом и составляют -алгебраические выражения:: - - >>> x+y+x-y - 2*x - - >>> (x+y)**2 - (x + y)**2 - - >>> ((x+y)**2).expand() - x**2 + 2*x*y + y**2 - -Переменные могут быть заменены на другие числа, переменные или выражения -функцией ``subs(old, new)``:: - - >>> ((x+y)**2).subs(x, 1) - (y + 1)**2 - - >>> ((x+y)**2).subs(x, y) - 4*y**2 - - >>> ((x+y)**2).subs(x, 1-y) - 1 - -Далее, на всем протяжении теста, мы будем предполагать, -что для всех примеров мы запустили команду:: - - >>> from sympy import init_printing - >>> init_printing(use_unicode=False, wrap_line=False, no_global=True) - -Это придаст более качественное отображение выражений при печати. -Смотрите раздел :ref:`printing-tutorial-ru` -ниже. Если же у вас установлен шрифт с юникодом, вы можете использовать -опцию use_unicode=True для еще более красивого вывода данных. -Подробнее о системе печати смотрите раздел :ref:`printing-tutorial-ru` ниже. - -Алгебра -======= - -Чтобы разложить выражение на простейшие дроби, используйте ``apart(expr, x)``:: - - >>> from sympy import apart - >>> from sympy.abc import x, y, z - - >>> 1/( (x+2)*(x+1) ) - 1 - --------------- - (x + 1)*(x + 2) - - >>> apart(1/( (x+2)*(x+1) ), x) - 1 1 - - ----- + ----- - x + 2 x + 1 - - >>> (x+1)/(x-1) - x + 1 - ----- - x - 1 - - >>> apart((x+1)/(x-1), x) - 2 - 1 + ----- - x - 1 - -Чтобы снова привести дроби к общему знаменателю, -используйте ``together(expr, x)``:: - - >>> from sympy import together - >>> together(1/x + 1/y + 1/z) - x*y + x*z + y*z - --------------- - x*y*z - - >>> together(apart((x+1)/(x-1), x), x) - x + 1 - ----- - x - 1 - - >>> together(apart(1/( (x+2)*(x+1) ), x), x) - 1 - --------------- - (x + 1)*(x + 2) - - -.. index:: calculus - -Вычисления -========== - -.. index:: limits - -Пределы -------- - -Пределы считаются в SymPy очень просто. Чтобы посчитать предел функции, -используйте ``limit(function, variable, point)``. -Например, чтобы вычислить предел f(x) при x -> 0, вам нужно ввести -``limit(f, x, 0)``:: - - >>> from sympy import limit, Symbol, sin, oo - >>> x = Symbol("x") - >>> limit(sin(x)/x, x, 0) - 1 - -также вы можете вычислить предел при x, стремящемся к бесконечности:: - - >>> limit(x, x, oo) - oo - - >>> limit(1/x, x, oo) - 0 - - >>> limit(x**x, x, 0) - 1 - -Для более сложных примеров вычисления пределов, -вы можете ознакомиться с тестовым файлом -`test_demidovich.py -`_ - -.. index:: differentiation, diff - -Дифференцирование ------------------ - -Вы можете продифференцировать любое выражение SymPy, -используя ``diff(func, var)``. Примеры:: - - >>> from sympy import diff, Symbol, sin, tan - >>> x = Symbol('x') - >>> diff(sin(x), x) - cos(x) - >>> diff(sin(2*x), x) - 2*cos(2*x) - - >>> diff(tan(x), x) - 2 - tan (x) + 1 - -Правильность производной вы можете проверить следующим способом:: - - >>> from sympy import limit - >>> from sympy.abc import delta - >>> limit((tan(x + delta) - tan(x))/delta, delta, 0) - 2 - tan (x) + 1 - -Производные более высших порядков вы можете вычислять, -используя функцию ``diff(func, var, n)``:: - - >>> diff(sin(2*x), x, 1) - 2*cos(2*x) - - >>> diff(sin(2*x), x, 2) - -4*sin(2*x) - - >>> diff(sin(2*x), x, 3) - -8*cos(2*x) - - -.. index:: - single: series expansion - single: expansion; series - -Разложение в ряд ----------------- - -Используйте ``.series(var, point, order)``:: - - >>> from sympy import Symbol, cos - >>> x = Symbol('x') - >>> cos(x).series(x, 0, 10) - 2 4 6 8 - x x x x / 10\ - 1 - -- + -- - --- + ----- + O\x / - 2 24 720 40320 - >>> (1/cos(x)).series(x, 0, 10) - 2 4 6 8 - x 5*x 61*x 277*x / 10\ - 1 + -- + ---- + ----- + ------ + O\x / - 2 24 720 8064 - -Еще один простой пример:: - - >>> from sympy import Integral, pprint - - >>> y = Symbol("y") - >>> e = 1/(x + y) - >>> s = e.series(x, 0, 5) - - >>> print(s) - 1/y - x/y**2 + x**2/y**3 - x**3/y**4 + x**4/y**5 + O(x**5) - >>> pprint(s) - 2 3 4 - 1 x x x x / 5\ - - - -- + -- - -- + -- + O\x / - y 2 3 4 5 - y y y y - -.. index:: integration - -Интегрирование --------------- - -SymPy поддерживает вычисление определенных и неопределенных интегралов -с помощью функции ``integrate()``. Она использует расширенный алгоритм -Risch-Norman, с некоторыми шаблонами и эвристиками. -Можно вычислять интегралы трансцендентных, простых и специальных функций:: - - >>> from sympy import integrate, erf, exp, sin, log, oo, pi, sinh, symbols - >>> x, y = symbols('x,y') - -Вы можете интегрировать простейшие функции:: - - >>> integrate(6*x**5, x) - 6 - x - >>> integrate(sin(x), x) - -cos(x) - >>> integrate(log(x), x) - x*log(x) - x - >>> integrate(2*x + sinh(x), x) - 2 - x + cosh(x) - -Примеры интегрирования некоторых специальных функций:: - - >>> integrate(exp(-x**2)*erf(x), x) - ____ 2 - \/ pi *erf (x) - -------------- - 4 - -Возможно также вычислить определенный интеграл:: - - >>> integrate(x**3, (x, -1, 1)) - 0 - >>> integrate(sin(x), (x, 0, pi/2)) - 1 - >>> integrate(cos(x), (x, -pi/2, pi/2)) - 2 - -Поддерживаются и несобственные интегралы:: - - >>> integrate(exp(-x), (x, 0, oo)) - 1 - >>> integrate(log(x), (x, 0, 1)) - -1 - -.. index:: - single: complex numbers - single: expansion; complex - -Комплексные числа ------------------ - -:: - - >>> from sympy import Symbol, exp, I - >>> x = Symbol("x") - >>> exp(I*x).expand() - I*x - e - >>> exp(I*x).expand(complex=True) - -im(x) -im(x) - I*e *sin(re(x)) + e *cos(re(x)) - >>> x = Symbol("x", real=True) - >>> exp(I*x).expand(complex=True) - I*sin(x) + cos(x) - -Функции -------- - -**тригонометрические**:: - - >>> from sympy import asin, asinh, cos, sin, sinh, symbols, I - >>> x, y = symbols('x,y') - - >>> sin(x+y).expand(trig=True) - sin(x)*cos(y) + sin(y)*cos(x) - - >>> cos(x+y).expand(trig=True) - -sin(x)*sin(y) + cos(x)*cos(y) - - >>> sin(I*x) - I*sinh(x) - - >>> sinh(I*x) - I*sin(x) - - >>> asinh(I) - I*pi - ---- - 2 - - >>> asinh(I*x) - I*asin(x) - - >>> sin(x).series(x, 0, 10) - 3 5 7 9 - x x x x / 10\ - x - -- + --- - ---- + ------ + O\x / - 6 120 5040 362880 - - >>> sinh(x).series(x, 0, 10) - 3 5 7 9 - x x x x / 10\ - x + -- + --- + ---- + ------ + O\x / - 6 120 5040 362880 - - >>> asin(x).series(x, 0, 10) - 3 5 7 9 - x 3*x 5*x 35*x / 10\ - x + -- + ---- + ---- + ----- + O\x / - 6 40 112 1152 - - >>> asinh(x).series(x, 0, 10) - 3 5 7 9 - x 3*x 5*x 35*x / 10\ - x - -- + ---- - ---- + ----- + O\x / - 6 40 112 1152 - -**сферические**:: - - >>> from sympy import Ylm - >>> from sympy.abc import theta, phi - - >>> Ylm(1, 0, theta, phi) - ___ - \/ 3 *cos(theta) - ---------------- - ____ - 2*\/ pi - - >>> Ylm(1, 1, theta, phi) - ___ I*phi - -\/ 6 *e *sin(theta) - ------------------------ - ____ - 4*\/ pi - - >>> Ylm(2, 1, theta, phi) - ____ I*phi - -\/ 30 *e *sin(theta)*cos(theta) - ------------------------------------ - ____ - 4*\/ pi - -**факториалы и гамма-функции**:: - - >>> from sympy import factorial, gamma, Symbol - >>> x = Symbol("x") - >>> n = Symbol("n", integer=True) - - >>> factorial(x) - x! - - >>> factorial(n) - n! - - >>> gamma(x + 1).series(x, 0, 3) # i.e. factorial(x) - / 2 2\ - 2 |EulerGamma pi | / 3\ - 1 - EulerGamma*x + x *|----------- + ---| + O\x / - \ 2 12/ - -**дзета-функции**:: - - >>> from sympy import zeta - >>> zeta(4, x) - zeta(4, x) - - >>> zeta(4, 1) - 4 - pi - --- - 90 - - >>> zeta(4, 2) - 4 - pi - -1 + --- - 90 - - >>> zeta(4, 3) - 4 - 17 pi - - -- + --- - 16 90 - - -**многочлены**:: - - >>> from sympy import assoc_legendre, chebyshevt, legendre, hermite - >>> chebyshevt(2, x) - 2 - 2*x - 1 - - >>> chebyshevt(4, x) - 4 2 - 8*x - 8*x + 1 - - >>> legendre(2, x) - 2 - 3*x 1 - ---- - - - 2 2 - - >>> legendre(8, x) - 8 6 4 2 - 6435*x 3003*x 3465*x 315*x 35 - ------- - ------- + ------- - ------ + --- - 128 32 64 32 128 - - >>> assoc_legendre(2, 1, x) - __________ - / 2 - -3*x*\/ - x + 1 - - >>> assoc_legendre(2, 2, x) - 2 - - 3*x + 3 - - >>> hermite(3, x) - 3 - 8*x - 12*x - -.. index:: equations; differential, diff, dsolve - -Дифференциальные уравнения --------------------------- - -В ``isympy``:: - - >>> from sympy import Function, Symbol, dsolve - >>> f = Function('f') - >>> x = Symbol('x') - >>> f(x).diff(x, x) + f(x) - 2 - d - f(x) + ---(f(x)) - 2 - dx - - >>> dsolve(f(x).diff(x, x) + f(x), f(x)) - f(x) = C1*sin(x) + C2*cos(x) - -.. index:: equations; algebraic, solve - -Алгебраические уравнения ------------------------- - -В ``isympy``:: - - >>> from sympy import solve, symbols - >>> x, y = symbols('x,y') - >>> solve(x**4 - 1, x) - [-1, 1, -I, I] - - >>> solve([x + 5*y - 2, -3*x + 6*y - 15], [x, y]) - {x: -3, y: 1} - -.. index:: linear algebra - -Линейная Алгебра -================ - -.. index:: Matrix - -Матрицы -------- - -Матрицы задаются с помощью конструктора Matrix:: - - >>> from sympy import Matrix, Symbol - >>> Matrix([[1,0], [0,1]]) - [1 0] - [ ] - [0 1] - -Также вы можете использовать в матрице символьные переменные:: - - >>> x = Symbol('x') - >>> y = Symbol('y') - >>> A = Matrix([[1,x], [y,1]]) - >>> A - [1 x] - [ ] - [y 1] - - >>> A**2 - [x*y + 1 2*x ] - [ ] - [ 2*y x*y + 1] - -Для того, чтобы получить больше информации, прочитайте, пожалуйста, -Руководство по Линейной Алгебре. - -.. index:: pattern matching, match, Wild, WildFunction - -Сопоставление с образцом -======================== - -Чтобы сопоставить выражения с образцами, используйте функцию ``.match()`` -в паре со вспомогательным классом ``Wild``. -Функция вернет словарь с необходимыми заменами, например:: - - >>> from sympy import Symbol, Wild - >>> x = Symbol('x') - >>> p = Wild('p') - >>> (5*x**2).match(p*x**2) - {p: 5} - - >>> q = Wild('q') - >>> (x**2).match(p*x**q) - {p: 1, q: 2} - -Если сопоставление не удалось, функция вернет``None``:: - - >>> print (x+1).match(p**x) - None - -Также можно использовать параметр exclude для исключения некоторых -значений из результата:: - - >>> p = Wild('p', exclude=[1,x]) - >>> print (x+1).match(x+p) # 1 is excluded - None - >>> print (x+1).match(p+1) # x is excluded - None - >>> print (x+1).match(x+2+p) # -1 is not excluded - {p_: -1} - -.. _printing-tutorial-ru: - -Печать -====== - -Существует несколько способов для того, чтобы вывести на экран выражения. - -**Стандартный** - -Стандартный способ представлен фукнцией ``str(expression)`` и работает -следующиим образом: - - >>> from sympy import Integral - >>> from sympy.abc import x - >>> print x**2 - x**2 - >>> print 1/x - 1/x - >>> print Integral(x**2, x) - Integral(x**2, x) - - -**Красивая печать (Pretty-print)** - -Это способ печати выражений, основанный на ascii-графике и реализованный -в функции ``pprint``: - - >>> from sympy import Integral, pprint - >>> from sympy.abc import x - >>> pprint(x**2) - 2 - x - >>> pprint(1/x) - 1 - - - x - >>> pprint(Integral(x**2, x)) - / - | - | 2 - | x dx - | - / - -Если у вас установлен шрифт с юникодом, он будет использовать Pretty-print -с юникодом по умолчанию. -Эту опцию можно отменить, используя ``use_unicode``: - - >>> pprint(Integral(x**2, x), use_unicode=True) - ⌠ - ⎮ 2 - ⎮ x dx - ⌡ - - -Также вы можете прочитать статью `Pretty Printing -`_ -из нашего Вики для изучения примеров Pretty-print с юникодом. - -Совет: Чтобы активировать Pretty-print по умолчанию в интерпретаторе Python, -используйте:: - - $ python - Python 2.5.2 (r252:60911, Jun 25 2008, 17:58:32) - [GCC 4.3.1] on linux2 - Type "help", "copyright", "credits" or "license" for more information. - >>> from sympy import init_printing, var, Integral - >>> init_printing(use_unicode=False, wrap_line=False, no_global=True) - >>> var("x") - x - >>> x**3/3 - 3 - x - -- - 3 - >>> Integral(x**2, x) #doctest: +NORMALIZE_WHITESPACE - / - | - | 2 - | x dx - | - / - -**Печать объектов Python** - - >>> from sympy.printing.python import python - >>> from sympy import Integral - >>> from sympy.abc import x - >>> print python(x**2) - x = Symbol('x') - e = x**2 - >>> print python(1/x) - x = Symbol('x') - e = 1/x - >>> print python(Integral(x**2, x)) - x = Symbol('x') - e = Integral(x**2, x) - - -**Печать в формате LaTeX** - - >>> from sympy import Integral, latex - >>> from sympy.abc import x - >>> latex(x**2) - x^{2} - >>> latex(x**2, mode='inline') - $x^{2}$ - >>> latex(x**2, mode='equation') - \begin{equation}x^{2}\end{equation} - >>> latex(x**2, mode='equation*') - \begin{equation*}x^{2}\end{equation*} - >>> latex(1/x) - \frac{1}{x} - >>> latex(Integral(x**2, x)) - \int x^{2}\, dx - -**MathML** - -:: - - >>> from sympy.printing.mathml import mathml - >>> from sympy import Integral, latex - >>> from sympy.abc import x - >>> print mathml(x**2) - x2 - >>> print mathml(1/x) - x-1 - -**Pyglet** - - >>> from sympy import Integral, preview - >>> from sympy.abc import x - >>> preview(Integral(x**2, x)) #doctest:+SKIP - -Появится окно pyglet с отрисованным выражением LaTeX: - -.. image:: pics/pngview1.png - -Примечания ----------- - -``isympy`` вызывает ``pprint`` автоматически, -поэтому Pretty-print будет использована по умолчанию. - -Также доступен модуль печати - ``sympy.printing``. -Через этот модуль доступны следующий функции печати: - -* ``pretty(expr)``, ``pretty_print(expr)``, ``pprint(expr)``: - Возвращает или выводит на экран, соответственно, Красивое представление - выражения ``expr``. Это то же самое, что и второй уровень представления, - описанный выше. - -* ``latex(expr)``, ``print_latex(expr)``: - Возвращает или выводит на экран, соответственно, - `LaTeX `_ -представление ``expr`` - -* ``mathml(expr)``, ``print_mathml(expr)``: - Возвращает или выводит на экран, соответственно, - `MathML `_ -представление ``expr``. - -* ``print_gtk(expr)``: Вывод ``expr`` в - `Gtkmathview `_, виджет GTK, - который отображает код MathML. Необходимо наличие пакета - `Gtkmathview `_ . - -Другие справочники -===================== - -Теперь самое время узнать больше о SymPy. Прочитайте -:ref:`Руководство пользователя SymPy ` и -:ref:`Описание модулей SymPy `. - -Также зайдите на `wiki.sympy.org `_, -сайт, который содержит множество полезных примеров, руководств и советов, -созданных нами и нашим сообществом. -Мы будем рады, если и вы внесете в него свой вклад. diff --git a/sympy/assumptions/handlers/ntheory.py b/sympy/assumptions/handlers/ntheory.py index 1dadf996104a..905ad8e0b25a 100644 --- a/sympy/assumptions/handlers/ntheory.py +++ b/sympy/assumptions/handlers/ntheory.py @@ -5,7 +5,6 @@ from sympy.assumptions.handlers import CommonHandler from sympy.ntheory import isprime from sympy.ntheory.residue_ntheory import int_tested -from sympy.functions.elementary.miscellaneous import round class AskPrimeHandler(CommonHandler): """ @@ -19,7 +18,7 @@ class AskPrimeHandler(CommonHandler): def _number(expr, assumptions): # helper method try: - i = int(round(expr)) + i = int(expr.round()) if not (expr - i).equals(0): raise TypeError except TypeError: @@ -105,7 +104,7 @@ class AskEvenHandler(CommonHandler): def _number(expr, assumptions): # helper method try: - i = int(round(expr)) + i = int(expr.round()) if not (expr - i).equals(0): raise TypeError except TypeError: diff --git a/sympy/assumptions/handlers/sets.py b/sympy/assumptions/handlers/sets.py index 48a7aae7e9a7..c6d0982b1046 100644 --- a/sympy/assumptions/handlers/sets.py +++ b/sympy/assumptions/handlers/sets.py @@ -3,7 +3,6 @@ """ from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler -from sympy.functions.elementary.miscellaneous import round class AskIntegerHandler(CommonHandler): """ @@ -15,7 +14,7 @@ class AskIntegerHandler(CommonHandler): def _number(expr, assumptions): # helper method try: - i = int(round(expr)) + i = int(expr.round()) if not (expr - i).equals(0): raise TypeError return True diff --git a/sympy/combinatorics/generators.py b/sympy/combinatorics/generators.py index 3d2a7e149c16..df5b80bed005 100644 --- a/sympy/combinatorics/generators.py +++ b/sympy/combinatorics/generators.py @@ -61,7 +61,11 @@ def alternating(n): def dihedral(n): """ - Generates the dihedral group of order 2n, D2n. + Generates the dihedral group of order 2n, Dn. + + The result is given as a subgroup of Sn, except for the special cases n=1 + (the group S2) and n=2 (the Klein 4-group) where that's not possible + and embeddings in S2 and S4 respectively are given. Examples ======== @@ -77,8 +81,17 @@ def dihedral(n): ======== cyclic """ - gen = range(n) - for i in xrange(n): - yield Permutation(gen) - yield Permutation(gen[::-1]) - gen = rotate_left(gen, 1) + if n == 1: + yield Permutation([0, 1]) + yield Permutation([1, 0]) + elif n == 2: + yield Permutation([0, 1, 2, 3]) + yield Permutation([1, 0, 3, 2]) + yield Permutation([2, 3, 0, 1]) + yield Permutation([3, 2, 1, 0]) + else: + gen = range(n) + for i in xrange(n): + yield Permutation(gen) + yield Permutation(gen[::-1]) + gen = rotate_left(gen, 1) diff --git a/sympy/combinatorics/permutations.py b/sympy/combinatorics/permutations.py index 65b9991c44e9..6e1e7835d797 100644 --- a/sympy/combinatorics/permutations.py +++ b/sympy/combinatorics/permutations.py @@ -887,6 +887,14 @@ def inversions(self): An inversion is where i > j but p[i] < p[j]. + For small length of p, it iterates over all i and j + values and calculates the number of inversions. + For large length of p, it uses a variation of merge + sort to calculate the number of inversions. + References + ========= + [1] http://www.cp.eng.chula.ac.th/~piak/teaching/algo/algo2008/count-inv.htm + Examples ======== @@ -894,6 +902,8 @@ def inversions(self): >>> p = Permutation([0,1,2,3,4,5]) >>> p.inversions() 0 + >>> Permutation([3,2,1,0]).inversions() + 6 See Also ======== @@ -902,11 +912,26 @@ def inversions(self): inversions = 0 a = self.array_form n = len(a) - for i in xrange(n - 1): - b = a[i] - for j in xrange(i + 1, n): - if b > a[j]: - inversions += 1 + if n < 130: + for i in xrange(n - 1): + b = a[i] + for c in a[i + 1:]: + if b > c: + inversions += 1 + else: + k = 1 + right = 0 + arr = a[:] + temp = a[:] + while k < n: + i = 0 + while i + k < n: + right = i + k * 2 - 1 + if right >= n: + right = n -1 + inversions += _merge(arr, temp, i, i+k, right) + i = i + k * 2; + k = k * 2 return inversions def conjugate(self, x): @@ -1603,3 +1628,35 @@ def _new_from_array_form(perm): p = Basic.__new__(Permutation, perm) p._array_form = perm return p + +def _merge(arr, temp, left, mid, right): + """ + Helper function for calculating inversions.This method is + for internal use only. + Merges two sorted arrays and calculates the inversion count. + """ + i = left + j = mid + k = left + inv_count = 0 + while i < mid and j <= right: + if arr[i] < arr[j]: + temp[k] = arr[i] + k += 1 + i += 1 + else: + temp[k] = arr[j] + k += 1 + j += 1 + inv_count += (mid -i) + while i < mid: + temp[k] = arr[i] + k += 1 + i += 1 + if j <= right: + k += right - j + 1 + j += right - j + 1 + arr[left:k + 1] = temp[left:k + 1] + else: + arr[left:right + 1] = temp[left:right + 1] + return inv_count diff --git a/sympy/combinatorics/tests/test_generators.py b/sympy/combinatorics/tests/test_generators.py index 0e26baa0a25b..7503f1085d34 100644 --- a/sympy/combinatorics/tests/test_generators.py +++ b/sympy/combinatorics/tests/test_generators.py @@ -32,6 +32,9 @@ def test_generators(): Permutation([3, 0, 1, 2]), Permutation([3, 0, 2, 1]), Permutation([3, 1, 0, 2]), \ Permutation([3, 1, 2, 0]), Permutation([3, 2, 0, 1]), Permutation([3, 2, 1, 0])] + assert list(dihedral(2)) == [Permutation([0, 1, 2, 3]), Permutation([1, 0, 3, 2]), \ + Permutation([2, 3, 0, 1]), Permutation([3, 2, 1, 0])] + assert list(dihedral(3)) == [Permutation([0, 1, 2]), Permutation([2, 1, 0]), Permutation([1, 2, 0]), \ Permutation([0, 2, 1]), Permutation([2, 0, 1]), Permutation([1, 0, 2])] diff --git a/sympy/combinatorics/tests/test_permutations.py b/sympy/combinatorics/tests/test_permutations.py index cfc07b3fde1e..176c23a912b0 100644 --- a/sympy/combinatorics/tests/test_permutations.py +++ b/sympy/combinatorics/tests/test_permutations.py @@ -44,6 +44,7 @@ def test_Permutation(): assert Permutation.from_inversion_vector(p.inversion_vector()) == p assert Permutation.from_inversion_vector(q.inversion_vector()).array_form\ == q.array_form + assert Permutation([i for i in range(500,-1,-1)]).inversions() == 125250 assert Permutation([0, 4, 1, 3, 2]).parity() == 0 assert Permutation([0, 1, 4, 3, 2]).parity() == 1 diff --git a/sympy/concrete/summations.py b/sympy/concrete/summations.py index e00f898ae00c..96e636957634 100644 --- a/sympy/concrete/summations.py +++ b/sympy/concrete/summations.py @@ -264,17 +264,9 @@ def fpoint(expr): g = g.diff(i, 2) return s + iterm, abs(term) - def _eval_subs(self, old, new): - if self == old: - return new - newlimits = [] - for lim in self.limits: - if lim[0] == old: - return self - newlimits.append( (lim[0],lim[1].subs(old,new),lim[2].subs(old,new)) ) - - return Sum(self.args[0].subs(old, new), *newlimits) - + def _eval_subs(self, old, new): # XXX this should be the same as Integral's + if any(old == v for v in self.variables): + return self def summation(f, *symbols, **kwargs): r""" diff --git a/sympy/core/__init__.py b/sympy/core/__init__.py index b1873986150b..484cc076cd4b 100644 --- a/sympy/core/__init__.py +++ b/sympy/core/__init__.py @@ -21,7 +21,8 @@ Function, Subs, expand, PoleError, count_ops, \ expand_mul, expand_log, expand_func,\ expand_trig, expand_complex, expand_multinomial, nfloat, expand_power_base -from sets import Set, Interval, Union, EmptySet, FiniteSet, ProductSet +from sets import (Set, Interval, Union, EmptySet, FiniteSet, ProductSet, + Intersection) from evalf import PrecisionExhausted, N from containers import Tuple, Dict from exprtools import gcd_terms, factor_terms, factor_nc diff --git a/sympy/core/add.py b/sympy/core/add.py index 057ccfc97753..45130d6c4069 100644 --- a/sympy/core/add.py +++ b/sympy/core/add.py @@ -414,7 +414,6 @@ def _eval_is_rational_function(self, syms): _eval_is_bounded = lambda self: self._eval_template_is_attr('is_bounded') _eval_is_commutative = lambda self: self._eval_template_is_attr('is_commutative') _eval_is_integer = lambda self: self._eval_template_is_attr('is_integer') - _eval_is_comparable = lambda self: self._eval_template_is_attr('is_comparable') def _eval_is_odd(self): l = [f for f in self.args if not (f.is_even==True)] @@ -437,6 +436,8 @@ def _eval_is_irrational(self): return False def _eval_is_positive(self): + if self.is_number: + return super(Add, self)._eval_is_positive() pos = nonneg = nonpos = unknown_sign = False unbounded = set() args = [a for a in self.args if not a.is_zero] @@ -486,6 +487,8 @@ def _eval_is_positive(self): return False def _eval_is_negative(self): + if self.is_number: + return super(Add, self)._eval_is_negative() neg = nonpos = nonneg = unknown_sign = False unbounded = set() args = [a for a in self.args if not a.is_zero] @@ -533,10 +536,8 @@ def _eval_is_negative(self): return False def _eval_subs(self, old, new): - if self == old: - return new - if isinstance(old, FunctionClass): - return self.__class__(*[s._eval_subs(old, new) for s in self.args ]) + if not old.is_Add: + return None coeff_self, terms_self = self.as_coeff_Add() coeff_old, terms_old = old.as_coeff_Add() @@ -547,19 +548,22 @@ def _eval_subs(self, old, new): if terms_self == -terms_old: # (2 + a).subs(-3 - a, y) -> -1 - y return Add(-new, coeff_self, coeff_old) - if old.is_Add: - coeff_self, terms_self = self.as_coeff_add() - coeff_old, terms_old = old.as_coeff_add() - - if len(terms_old) < len(terms_self): # (a+b+c+d).subs(b+c,x) -> a+x+d - self_set = set(terms_self) - old_set = set(terms_old) + args_old, args_self = Add.make_args(terms_old), Add.make_args(terms_self) + if len(args_old) < len(args_self): # (a+b+c+d).subs(b+c,x) -> a+x+d + self_set = set(args_self) + old_set = set(args_old) if old_set < self_set: ret_set = self_set - old_set - return Add(new, coeff_self, -coeff_old, *[s._eval_subs(old, new) for s in ret_set]) + return Add(new, coeff_self, -coeff_old, + *[s._subs(old, new) for s in ret_set]) - return self.__class__(*[s._eval_subs(old, new) for s in self.args]) + args_old = Add.make_args(-terms_old) # (a+b+c+d).subs(-b-c,x) -> a-x+d + old_set = set(args_old) + if old_set < self_set: + ret_set = self_set - old_set + return Add(-new, coeff_self, coeff_old, + *[s._subs(old, new) for s in ret_set]) def removeO(self): args = [a for a in self.args if not a.is_Order] diff --git a/sympy/core/assumptions.py b/sympy/core/assumptions.py index 72106156218f..076ac1c6e771 100644 --- a/sympy/core/assumptions.py +++ b/sympy/core/assumptions.py @@ -1,4 +1,3 @@ -from sympy.core.compatibility import cmp from sympy.core.facts import FactRules from sympy.core.core import BasicMeta @@ -58,27 +57,10 @@ ]) _assume_defined = _assume_rules.defined_facts.copy() -_assume_defined.add('comparable') _assume_defined.add('polar') _assume_defined = frozenset(_assume_defined) -################################### -# positive/negative from .evalf() # -################################### - -# properties that indicate ordering on real axis -_real_ordering = set(['negative', 'nonnegative', 'positive', 'nonpositive']) - -# what can be said from cmp(x.evalf(),0) -# if x.evalf() is zero we can say nothing so nonpositive is the same as negative -_real_cmp0_table= { - 'positive': {1: True, -1: False, 0: None}, - 'negative': {1: False, -1: True, 0: None}, - } -_real_cmp0_table['nonpositive'] = _real_cmp0_table['negative'] -_real_cmp0_table['nonnegative'] = _real_cmp0_table['positive'] - class CycleDetected(Exception): """(internal) used to detect cycles when evaluating assumptions through prerequisites @@ -196,7 +178,7 @@ class AssumeMixin(object): - infinitesimal - object value is infinitesimal - Examples rules: + Example rules: positive=T -> nonpositive=F, real=T real=T & positive=F -> nonpositive=T @@ -334,15 +316,6 @@ def _what_known_about(self, k): so in the latter case if we are looking at what 'even' value is, 'integer' and 'odd' facts will be asked. - - 3. evalf() for comparable - ------------------------- - - as a last resort for comparable objects we get their numerical value - -- this helps to determine facts like 'positive' and 'negative' - - - In all cases when we settle on some fact value, it is given to _learn_new_facts to deduce all its implications, and also the result is cached in ._assumptions for later quick access. @@ -385,17 +358,6 @@ def _what_known_about(self, k): finally: seen.pop() - # For positive/negative try to ask evalf - if k in _real_ordering and self.is_comparable: - e = self.evalf(1) - - if e.is_Number: - a = _real_cmp0_table[k][cmp(e, 0)] - - if a is not None: - self._learn_new_facts( ((k,a),) ) - return a - # No result -- unknown # cache it (NB ._learn_new_facts(k, None) to learn other properties, # and because assumptions may not be detached) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index cd214b743fcc..22a1e1c48085 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -189,7 +189,7 @@ def _hashable_content(self): def compare(self, other): """ - Return -1,0,1 if the object is smaller, equal, or greater than other. + Return -1, 0, 1 if the object is smaller, equal, or greater than other. Not in the mathematical sense. If the object is of a different type from the "other" then their classes are ordered according to @@ -209,22 +209,26 @@ def compare(self, other): """ # all redefinitions of __cmp__ method should start with the # following three lines: - if self is other: return 0 + if self is other: + return 0 c = cmp(self.__class__, other.__class__) - if c: return c + if c: + return c # st = self._hashable_content() ot = other._hashable_content() - c = cmp(len(st),len(ot)) - if c: return c - for l,r in zip(st,ot): + c = cmp(len(st), len(ot)) + if c: + return c + for l, r in zip(st, ot): if isinstance(l, Basic): c = l.compare(r) elif isinstance(l, frozenset): c = 0 else: c = cmp(l, r) - if c: return c + if c: + return c return 0 @staticmethod @@ -626,6 +630,16 @@ def is_number(self): # should be overriden by subclasses return False + @property + def is_comparable(self): + is_real = self.is_real + if is_real is False: + return False + is_number = self.is_number + if is_number is False: + return False + return self.is_real and self.is_number + @property def func(self): """ @@ -737,154 +751,296 @@ def as_content_primitive(self, radical=False): """ return S.One, self - def subs(self, *args): + def subs(self, *args, **kwargs): """ - Substitutes an expression. - - Calls either _subs_old_new, _subs_dict or _subs_list depending - if you give it two arguments (old, new), a dictionary or a list. + Substitutes old for new in an expression after sympifying args. + + `args` is either: + - two arguments, e.g. foo.subs(old, new) + - one iterable argument, e.g. foo.subs(iterable). The iterable may be + o an iterable container with (old, new) pairs. In this case the + replacements are processed in the order given with successive + patterns possibly affecting replacements already made. + o a dict or set whose key/value items correspond to old/new pairs. + In this case the old/new pairs will be sorted by op count and in + case of a tie, by number of args and the default_sort_key. The + resulting sorted list is then processed as an iterable container + (see previous). + + If the keyword ``simultaneous`` is True, the subexpressions will not be + evaluated until all the substitutions have been made. Examples ======== - >>> from sympy import pi + >>> from sympy import pi, exp >>> from sympy.abc import x, y >>> (1 + x*y).subs(x, pi) pi*y + 1 >>> (1 + x*y).subs({x:pi, y:2}) 1 + 2*pi - >>> (1 + x*y).subs([(x,pi), (y,2)]) + >>> (1 + x*y).subs([(x, pi), (y, 2)]) 1 + 2*pi - - >>> (x + y).subs([(y,x**2), (x,2)]) + >>> reps = [(y, x**2), (x, 2)] + >>> (x + y).subs(reps) 6 - >>> (x + y).subs([(x,2), (y,x**2)]) + >>> (x + y).subs(reversed(reps)) x**2 + 2 + + >>> (x**2 + x**4).subs(x**2, y) + y**2 + y + + To replace only the x**2 but not the x**4, use xreplace: + + >>> (x**2 + x**4).xreplace({x**2: y}) + x**4 + y + + To delay evaluation until all substitutions have been made, + set the keyword ``simultaneous`` to True: + + >>> (x/y).subs([(x, 0), (y, 0)]) + 0 + >>> (x/y).subs([(x, 0), (y, 0)], simultaneous=True) + nan + + This has the added feature of not allowing subsequent substitutions + to affect those already made: + + >>> ((x + y)/y).subs({x + y: y, y: x + y}) + 1 + >>> ((x + y)/y).subs({x + y: y, y: x + y}, simultaneous=True) + y/(x + y) + + In order to obtain a canonical result, unordered iterables are + sorted by count_op length, number of arguments and by the + default_sort_key to break any ties. All other iterables are left + unsorted. + + >>> from sympy import sqrt, sin, cos, exp + >>> from sympy.abc import a, b, c, d, e + + >>> A = (sqrt(sin(2*x)), a) + >>> B = (sin(2*x), b) + >>> C = (cos(2*x), c) + >>> D = (x, d) + >>> E = (exp(x), e) + + >>> expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) + + >>> expr.subs(dict([A,B,C,D,E])) + a*c*sin(d*e) + b + + See Also + ======== + replace: replacement capable of doing wildcard-like matching, + parsing of match, and conditional replacements + xreplace: exact node replacement in expr tree; also capable of + using matching rules + """ + from sympy.core.expr import Expr + from sympy.core.containers import Dict + from sympy.utilities import default_sort_key, sift + from sympy.core.function import Function, Derivative + from sympy.core.symbol import Symbol + + unordered = False if len(args) == 1: sequence = args[0] - if isinstance(sequence, dict): - return self._subs_dict(sequence) - elif iterable(sequence): - return self._subs_list(sequence) - else: - raise TypeError("Not an iterable container") + if isinstance(sequence, set): + unordered = True + elif isinstance(sequence, (Dict, dict)): + unordered = True + sequence = sequence.items() + elif not iterable(sequence): + from sympy.utilities.misc import filldedent + raise ValueError(filldedent(""" + When a single argument is passed to subs + it should be an iterable of (old, new) tuples.""")) elif len(args) == 2: - old, new = args - return self._subs_old_new(old, new) + sequence = [args] + else: + raise ValueError("subs accepts either 1 or 2 arguments") + + sequence = list(sequence) + for i in range(len(sequence)): + o, n = sequence[i] + so, sn = sympify(o), sympify(n) + if not isinstance(so, Basic): + if type(o) is str: + so = C.Symbol(o) + sequence[i] = (so, sn) + if _aresame(so, sn): + sequence[i] = None + continue + sequence = filter(None, sequence) + + if unordered: + sequence = dict(sequence) + if not all(k.is_Atom for k in sequence): + d = {} + for o, n in sequence.iteritems(): + try: + ops = o.count_ops(), len(o.args) + except TypeError: + ops = (0, 0) + d.setdefault(ops, []).append((o, n)) + newseq = [] + for k in sorted(d.keys(), reverse=True): + newseq.extend(sorted([v[0] for v in d[k]], key=default_sort_key)) + sequence = [(k, sequence[k]) for k in newseq] + del newseq, d + else: + sequence = sorted([(k, v) for (k, v) in sequence.iteritems()], + key=default_sort_key) + + if kwargs.pop('simultaneous', False): # XXX should this be the default for dict subs? + reps = {} + rv = self + for old, new in sequence: + d = C.Dummy() + rv = rv._subs(old, d) + reps[d] = new + if not isinstance(rv, Basic): + break + return rv.xreplace(reps) else: - raise TypeError("subs accepts either 1 or 2 arguments") + rv = self + for old, new in sequence: + rv = rv._subs(old, new) + if not isinstance(rv, Basic): + break + return rv @cacheit - def _subs_old_new(self, old, new): - """Substitutes an expression old -> new.""" - old = sympify(old) - new = sympify(new) - return self._eval_subs(old, new) + def _subs(self, old, new, **hints): + """Substitutes an expression old -> new. - def _eval_subs(self, old, new): - if self == old: - return new - else: - return self.func(*[arg._eval_subs(old, new) for arg in self.args]) + If self is not equal to old then _eval_subs is called. + If _eval_subs doesn't want to make any special replacement + then a None is received which indicates that the fallback + should be applied wherein a search for replacements is made + amongst the arguments of self. - def _subs_list(self, sequence): - """ - Performs an order sensitive substitution from the - input sequence list. + >>> from sympy import Basic, Add, Mul + >>> from sympy.abc import x, y, z Examples ======== - >>> from sympy.abc import x, y - >>> (x+y)._subs_list( [(x, 3), (y, x**2)] ) - x**2 + 3 - >>> (x+y)._subs_list( [(y, x**2), (x, 3) ] ) - 12 + Add's _eval_subs knows how to target x + y in the following + so it makes the change: - """ - result = self - for old, new in sequence: - if hasattr(result, 'subs'): - result = result.subs(old, new) - return result + >>> (x + y + z).subs(x + y, 1) + z + 1 - def _subs_dict(self, sequence): - """Performs sequential substitution. + Add's _eval_subs doesn't need to know how to find x + y in + the following: - Given a collection of key, value pairs, which correspond to - old and new expressions respectively, substitute all given - pairs handling properly all overlapping keys (according to - 'in' relation). + >>> Add._eval_subs(z*(x + y) + 3, x + y, 1) is None + True - We have to use naive O(n**2) sorting algorithm, as 'in' - gives only partial order and all asymptotically faster - fail (depending on the initial order). + The returned None will cause the fallback routine to traverse the args and + pass the z*(x + y) arg to Mul where the change will take place and the + substitution will succeed: - >>> from sympy import sqrt, sin, cos, exp - >>> from sympy.abc import x, y + >>> (z*(x + y) + 3).subs(x + y, 1) + z + 3 + + ** Developers Notes ** + + An _eval_subs routine for a class should be written if: - >>> from sympy.abc import a, b, c, d, e + 1) any arguments are not instances of Basic (e.g. bool, tuple); - >>> A = (sqrt(sin(2*x)), a) - >>> B = (sin(2*x), b) - >>> C = (cos(2*x), c) - >>> D = (x, d) - >>> E = (exp(x), e) + 2) some arguments should not be targeted (as in integration + variables); - >>> expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) + 3) if there is something other than a literal replacement + that should be attempted (as in Piecewise where the condition + may be updated without doing a replacement). - >>> expr._subs_dict([A,B,C,D,E]) - a*c*sin(d*e) + b + If it is overridden, here are some special cases that might arise: + 1) If it turns out that no special change was made and all + the original sub-arguments should be checked for + replacements then None should be returned. + + 2) If it is necessary to do substitutions on a portion of + the expression then _subs should be called. _subs will + handle the case of any sub-expression being equal to old + (which usually would not be the case) while its fallback + will handle the recursion into the sub-arguments. For + example, after Add's _eval_subs removes some matching terms + it must process the remaining terms so it calls _subs + on each of the un-matched terms and then adds them + onto the terms previously obtained. + + 3) If the initial expression should remain unchanged then + the original expression should be returned. (Whenever an + expression is returned, modified or not, no further + substitution of old -> new is attempted.) Sum's _eval_subs + routine uses this strategy when a substitution is attempted + on any of its summation variables. """ - sequence = sympify(sequence) - if isinstance(sequence, dict): - sequence = sequence.items() + def fallback(self, old, new): + """ + Try to replace old with new in any of self's arguments. + """ + hit = False + args = list(self.args) + for i, arg in enumerate(args): + if not hasattr(arg, '_eval_subs'): + continue + arg = arg._subs(old, new, **hints) + if arg is not args[i]: + hit = True + args[i] = arg + if hit: + return self.func(*args) + return self - subst = [] + if _aresame(self, old): + return new - for pattern in sequence: - for i, (expr, _) in enumerate(subst): - if expr.has(pattern[0]): - subst.insert(i, pattern) - break - else: - subst.append(pattern) - subst.reverse() + rv = self._eval_subs(old, new) + if rv is None: + rv = fallback(self, old, new) + return rv - return self._subs_list(subst) + def _eval_subs(self, old, new): + """Override this stub if you want to do anything more than + attempt a replacement of old with new in the arguments of self. + + See also: _subs + """ + return None def xreplace(self, rule): """ Replace occurrences of objects within the expression. Parameters - ---------- + ========== rule : dict-like Expresses a replacement rule Returns - ------- + ======= xreplace : the result of the replacement Examples - -------- - >>> from sympy import symbols, pi - >>> x,y, z = symbols('x y z') - >>> (1+x*y).xreplace({x: pi}) + ======== + >>> from sympy import symbols, pi, exp + >>> x, y, z = symbols('x y z') + >>> (1 + x*y).xreplace({x: pi}) pi*y + 1 - >>> (1+x*y).xreplace({x:pi, y:2}) + >>> (1 + x*y).xreplace({x:pi, y:2}) 1 + 2*pi - Notes - ----- - This method operates at a low level and considers only the objects that - appear explicitly as nodes in the expression tree. It is unaware of any - specific meaning attached to an object or its arguments. For instance, - a product of several factors will only be substituted if it matches - exactly a key of the dictionary: + Replacements occur only if an entire node in the expression tree is + matched: >>> (x*y + z).xreplace({x*y: pi}) z + pi @@ -894,6 +1050,31 @@ def xreplace(self, rule): y >>> (2*2*x).xreplace({2*x: y, x: z}) 4*z + >>> (x + y + 2).xreplace({x + y: 2}) + x + y + 2 + >>> (x + 2 + exp(x + 2)).xreplace({x + 2: y}) + x + exp(y) + 2 + + xreplace doesn't differentiate between free and bound symbols. In the + following, subs(x, y) would not change x since it is a bound symbol, + but xreplace does: + + >>> from sympy import Integral + >>> Integral(x, (x, 1, 2*x)).xreplace({x: y}) + Integral(y, (y, 1, 2*y)) + + Trying to replace x with an expression raises an error: + + >>> Integral(x, (x, 1, 2*x)).xreplace({x: 2*y}) #doctest: +SKIP + ValueError: Invalid limits given: ((2*y, 1, 4*y),) + + See Also + ======== + replace: replacement capable of doing wildcard-like matching, + parsing of match, and conditional replacements + subs: substitution of subexpressions as defined by the objects + themselves. + """ if self in rule: return rule[self] @@ -1017,46 +1198,54 @@ def replace(self, query, value, map=False): possible combinations of queries and replacement values is listed below: - 1.1. type -> type - obj.replace(sin, tan) - 1.2. type -> func - obj.replace(sin, lambda expr, arg: ...) + Examples + ======== + >>> from sympy import log, sin, cos, tan, Wild + >>> from sympy.abc import x, y + >>> f = log(sin(x)) + tan(sin(x**2)) - 2.1. expr -> expr - obj.replace(sin(a), tan(a)) - 2.2. expr -> func - obj.replace(sin(a), lambda a: ...) + 1.1. type -> type + obj.replace(sin, tan) - 3.1. func -> func - obj.replace(lambda expr: ..., lambda expr: ...) + >>> f.replace(sin, cos) + log(cos(x)) + tan(cos(x**2)) + >>> sin(x).replace(sin, cos, map=True) + (cos(x), {sin(x): cos(x)}) - Examples - ======== + 1.2. type -> func + obj.replace(sin, lambda arg: ...) - >>> from sympy import log, sin, cos, tan, Wild - >>> from sympy.abc import x + >>> f.replace(sin, lambda arg: sin(2*arg)) + log(sin(2*x)) + tan(sin(2*x**2)) - >>> f = log(sin(x)) + tan(sin(x**2)) + 2.1. expr -> expr + obj.replace(sin(a), tan(a)) - >>> f.replace(sin, cos) - log(cos(x)) + tan(cos(x**2)) - >>> f.replace(sin, lambda arg: sin(2*arg)) - log(sin(2*x)) + tan(sin(2*x**2)) + >>> a = Wild('a') + >>> f.replace(sin(a), tan(a)) + log(tan(x)) + tan(tan(x**2)) - >>> sin(x).replace(sin, cos, map=True) - (cos(x), {sin(x): cos(x)}) + 2.2. expr -> func + obj.replace(sin(a), lambda a: ...) - >>> a = Wild('a') + >>> f.replace(sin(a), cos(a)) + log(cos(x)) + tan(cos(x**2)) + >>> f.replace(sin(a), lambda a: sin(2*a)) + log(sin(2*x)) + tan(sin(2*x**2)) - >>> f.replace(sin(a), cos(a)) - log(cos(x)) + tan(cos(x**2)) - >>> f.replace(sin(a), lambda a: sin(2*a)) - log(sin(2*x)) + tan(sin(2*x**2)) + 3.1. func -> func + obj.replace(lambda expr: ..., lambda expr: ...) - >>> g = 2*sin(x**3) + >>> g = 2*sin(x**3) + >>> g.replace(lambda expr: expr.is_Number, lambda expr: expr**2) + 4*sin(x**9) - >>> g.replace(lambda expr: expr.is_Number, lambda expr: expr**2) - 4*sin(x**9) + See Also + ======== + subs: substitution of subexpressions as defined by the objects + themselves. + xreplace: exact node replacement in expr tree; also capable of + using matching rules """ if isinstance(query, type): @@ -1335,12 +1524,6 @@ def matches(self, expr, repl_dict={}): if self == expr: return repl_dict - def _eval_subs(self, old, new): - if self == old: - return new - else: - return self - def xreplace(self, rule): return rule.get(self, self) @@ -1359,3 +1542,92 @@ def class_key(cls): def sort_key(self, order=None): from sympy.core import S return self.class_key(), (1, (str(self),)), S.One.sort_key(), S.One + +def _aresame(a, b): + """Return True if a and b are structurally the same, else False. + + Examples + ======== + + To SymPy, 2.0 == 2: + + >>> from sympy import S, Symbol, cos, sin + >>> 2.0 == S(2) + True + + The Basic.compare method will indicate that these are not the same, but + the same method allows symbols with different assumptions to compare the + same: + + >>> S(2).compare(2.0) + -1 + >>> Symbol('x').compare(Symbol('x', positive=True)) + 0 + + The Basic.compare method will not work with instances of FunctionClass: + + >>> sin.compare(cos) + Traceback (most recent call last): + File "", line 1, in + TypeError: unbound method compare() must be called with sin instance as first ar + gument (got FunctionClass instance instead) + + Since a simple 'same or not' result is sometimes useful, this routine was + written to provide that query. + + """ + from sympy.utilities.iterables import preorder_traversal + from itertools import izip + + try: + if a.compare(b) == 0 and a.is_Symbol and b.is_Symbol: + return a.assumptions0 == b.assumptions0 + except (TypeError, AttributeError): + pass + + for i, j in izip(preorder_traversal(a), preorder_traversal(b)): + if i == j and type(i) == type(j): + continue + return False + return True + +def _atomic(e): + """Return atom-like quantities as far as substitution is + concerned: Derivatives, Functions and Symbols. Don't + return any 'atoms' that are inside such quantities unless + they also appear outside, too. + + Examples + ======== + >>> from sympy import Derivative, Function, cos + >>> from sympy.abc import x, y + >>> from sympy.core.basic import _atomic + >>> f = Function('f') + >>> _atomic(x + y) + set([x, y]) + >>> _atomic(x + f(y)) + set([x, f(y)]) + >>> _atomic(Derivative(f(x), x) + cos(x) + y) + set([y, cos(x), Derivative(f(x), x)]) + + """ + from sympy import Derivative, Function, Symbol + from sympy.utilities.iterables import preorder_traversal + pot = preorder_traversal(e) + seen = set() + try: + free = e.free_symbols + except AttributeError: + return set([e]) + atoms = set() + for p in pot: + if p in seen: + pot.skip() + continue + seen.add(p) + if isinstance(p, Symbol) and p in free: + atoms.add(p) + elif isinstance(p, (Derivative, Function)): + pot.skip() + atoms.add(p) + return atoms diff --git a/sympy/core/containers.py b/sympy/core/containers.py index b59de144ef5c..2b85e4120737 100644 --- a/sympy/core/containers.py +++ b/sympy/core/containers.py @@ -6,8 +6,8 @@ They are supposed to work seamlessly within the SymPy framework. """ -from basic import Basic -from sympify import sympify +from sympy.core.basic import Basic +from sympy.core.sympify import sympify, converter from sympy.utilities.iterables import iterable class Tuple(Basic): @@ -83,6 +83,8 @@ def _to_mpmath(self, prec): def __lt__(self, other): return self.args < other.args +converter[tuple] = lambda tup: Tuple(*tup) + def tuple_wrapper(method): """ Decorator that converts any tuple in the function arguments into a Tuple. diff --git a/sympy/core/expr.py b/sympy/core/expr.py index b148c4438cf7..97607fda323b 100644 --- a/sympy/core/expr.py +++ b/sympy/core/expr.py @@ -5,8 +5,10 @@ from decorators import _sympifyit, call_highest_priority from cache import cacheit from compatibility import reduce, SymPyDeprecationWarning +from sympy.mpmath.libmp import mpf_log, prec_to_dps from collections import defaultdict +from math import log10, ceil class Expr(Basic, EvalfMixin): __slots__ = [] @@ -154,7 +156,6 @@ def __rmod__(self, other): return Mod(other, self) def __int__(self): - from sympy import round # Although we only need to round to the units position, we'll # get one more digit so the extra testing below can be avoided # unless the rounded value rounded to an integer, e.g. if an @@ -167,7 +168,9 @@ def __int__(self): # off by one. So...if our round value is the same as the int value # (regardless of how much extra work we do to calculate extra decimal # places) we need to test whether we are off by one. - r = round(self, 1) + r = self.round(1) + if not r.is_Number: + raise TypeError("can't convert complex to int") i = int(r) if not i: return 0 @@ -525,6 +528,22 @@ def equals(self, other, failing_expression=False): return diff return None + def _eval_is_positive(self): + if self.is_real and self.is_number: + n = self.evalf(1) + if n > 0: + return True + elif n < 0: + return False + + def _eval_is_negative(self): + if self.is_real and self.is_number: + n = self.evalf(1) + if n < 0: + return True + elif n > 0: + return False + def _eval_interval(self, x, a, b): """ Returns evaluation over an interval. For most functions this is: @@ -2653,6 +2672,94 @@ def invert(self, g): from sympy.polys import invert return invert(self, g) + def round(self, p=0): + """Return x rounded to the given decimal place. + + If a complex number would results, apply round to the real + and imaginary components of the number. + + Examples + ======== + >>> from sympy import pi, E, I, S, Add, Mul, Number + >>> S(10.5).round() + 11. + >>> pi.round() + 3. + >>> pi.round(2) + 3.14 + >>> (2*pi + E*I).round() #doctest: +SKIP + 6. + 3.*I + + The round method has a chopping effect: + + >>> (2*pi + I/10).round() + 6. + >>> (pi/10 + 2*I).round() #doctest: +SKIP + 2.*I + >>> (pi/10 + E*I).round(2) + 0.31 + 2.72*I + + Notes + ===== + + Do not confuse the Python builtin function, round, with the + SymPy method of the same name. The former always returns a float + (or raises an error if applied to a complex value) while the + latter returns either a Number or a complex number: + + >>> isinstance(round(S(123), -2), Number) + False + >>> isinstance(S(123).round(-2), Number) + True + >>> isinstance((3*I).round(), Mul) + True + >>> isinstance((1 + 3*I).round(), Add) + True + + """ + from sympy.functions.elementary.exponential import log + + x = self + if not x.is_number: + raise TypeError('%s is not a number' % x) + if not x.is_real: + i, r = x.as_real_imag() + return i.round(p) + S.ImaginaryUnit*r.round(p) + if not x: + return x + p = int(p) + + precs = [f._prec for f in x.atoms(C.Float)] + dps = prec_to_dps(max(precs)) if precs else None + + xpos = abs(x.n()) + try: + mag_first_dig = int(ceil(log10(xpos))) + except (ValueError, OverflowError): + mag_first_dig = int(ceil(C.Float(mpf_log(xpos._mpf_, 53))/log(10))) + # check that we aren't off by 1 + if (xpos/10**mag_first_dig) >= 1: + mag_first_dig += 1 + assert .1 <= (xpos/10**mag_first_dig) < 1 + allow = digits_needed = mag_first_dig + p + if dps is not None and allow > dps: + allow = dps + mag = Pow(10, p) # magnitude needed to bring digit p to units place + x += 1/(2*mag) # add the half for rounding + i10 = 10*mag*x.n((dps if dps is not None else digits_needed) + 1) + rv = Integer(i10)//10 + q = 1 + if p > 0: + q = mag + elif p < 0: + rv /= mag + rv = Rational(rv, q) + if rv.is_Integer: + # use str or else it won't be a float + return C.Float(str(rv), digits_needed) + else: + return C.Float(rv, allow) + class AtomicExpr(Atom, Expr): """ A parent class for object which are both atoms and Exprs. @@ -2670,9 +2777,6 @@ def _eval_derivative(self, s): return S.One return S.Zero - def as_numer_denom(self): - return self, S.One - def _eval_is_polynomial(self, syms): return True @@ -2690,3 +2794,4 @@ def _eval_nseries(self, x, n, logx): from sympify import sympify from symbol import Wild from exprtools import factor_terms +from numbers import Integer, Rational diff --git a/sympy/core/function.py b/sympy/core/function.py index a8a3839aa244..2d3edaca25ff 100644 --- a/sympy/core/function.py +++ b/sympy/core/function.py @@ -161,15 +161,11 @@ def func(self): return self.__class__ def _eval_subs(self, old, new): - if self == old: - return new - elif old.is_Function and new.is_Function: - if old == self.func: - nargs = len(self.args) - if (nargs == new.nargs or new.nargs is None or - (isinstance(new.nargs, tuple) and nargs in new.nargs)): - return new(*self.args) - return self.func(*[s.subs(old, new) for s in self.args]) + if (old.is_Function and new.is_Function and + old == self.func and + (self.nargs == new.nargs or not new.nargs or + isinstance(new.nargs, tuple) and self.nargs in new.nargs)): + return new(*self.args) @deprecated def __contains__(self, obj): @@ -330,18 +326,6 @@ def _eval_evalf(self, prec): return Expr._from_mpmath(v, prec) - def _eval_is_comparable(self): - if self.is_Function: - r = True - for s in self.args: - c = s.is_comparable - if c is None: - return - if not c: - r = False - return r - return - def _eval_derivative(self, s): # f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s) i = 0 @@ -1136,15 +1120,13 @@ def free_symbols(self): return self.expr.free_symbols def _eval_subs(self, old, new): - if self==old: - return new # Subs should only be used in situations where new is not a valid # variable for differentiating wrt. Previously, Subs was used for # anything that was not a Symbol, but that was too broad. if old in self.variables and not new._diff_wrt: # Issue 1620 return Subs(self, old, new) - return Derivative(*map(lambda x: x._eval_subs(old, new), self.args)) + return Derivative(*map(lambda x: x._subs(old, new), self.args)) def _eval_lseries(self, x): dx = self.args[1:] diff --git a/sympy/core/mul.py b/sympy/core/mul.py index e2275e83ab70..394063328946 100644 --- a/sympy/core/mul.py +++ b/sympy/core/mul.py @@ -583,11 +583,11 @@ def _eval_power(b, e): # out with a negative sign added and a negative left behind # in the unexpanded terms if there were an odd number of # negatives. + if coeff.is_negative: + coeff = -coeff + neg.append(S.NegativeOne) if neg: neg = [-w for w in neg] - if coeff.is_negative: - coeff = -coeff - unk.append(S.NegativeOne) if len(neg) % 2: unk.append(S.NegativeOne) @@ -983,7 +983,6 @@ def _eval_is_rational_function(self, syms): _eval_is_bounded = lambda self: self._eval_template_is_attr('is_bounded') _eval_is_commutative = lambda self: self._eval_template_is_attr('is_commutative') _eval_is_integer = lambda self: self._eval_template_is_attr('is_integer') - _eval_is_comparable = lambda self: self._eval_template_is_attr('is_comparable') def _eval_is_polar(self): has_polar = any(arg.is_polar for arg in self.args) @@ -1158,20 +1157,12 @@ def _eval_is_even(self): return False def _eval_subs(self, old, new): + if not old.is_Mul: + return None from sympy import sign from sympy.simplify.simplify import powdenest - if self == old: - return new - - - def fallback(): - """Return this value when partial subs has failed.""" - - return self.__class__(*[s._eval_subs(old, new) for s in - self.args]) - def breakup(eq): """break up powers assuming (not checking) that eq is a Mul: b**(Rational*e) -> b**e, Rational @@ -1216,8 +1207,18 @@ def ndiv(a, b): return int(a/b) return 0 - if not old.is_Mul: - return fallback() + # give Muls in the denominator a chance to be changed (see issue 2552) + # rv will be the default return value + rv = None + n, d = self.as_numer_denom() + if d is not S.One: + self2 = n._subs(old, new)/d._subs(old, new) + if not self2.is_Mul: + return self2._subs(old, new) + if self2 != self: + self = rv = self2 + + # Now continue with regular substitution. # handle the leading coefficient and use it to decide if anything # should even be started; we always know where to find the Rational @@ -1233,7 +1234,7 @@ def ndiv(a, b): co_xmul = True if not co_xmul: - return fallback() + return rv (c, nc) = breakup(self) (old_c, old_nc) = breakup(old) @@ -1245,16 +1246,31 @@ def ndiv(a, b): c[co_xmul] = S.One old_c.pop(co_old) - # Do some quick tests to see whether we can succeed: - # 1) check for more non-commutative or 2) commutative terms - # 3) ... unmatched non-commutative bases - # 4) ... unmatched commutative terms - # 5) and finally differences in sign - if len(old_nc) > len(nc) or len(old_c) > len(c) or \ - set(_[0] for _ in old_nc).difference(set(_[0] for _ in nc)) or \ - set(old_c).difference(set(c)) or \ - any(sign(c[b]) != sign(old_c[b]) for b in old_c): - return fallback() + # do quick tests to see if we can't succeed + + ok = True + if ( + # more non-commutative terms + len(old_nc) > len(nc)): + ok = False + elif ( + # more commutative terms + len(old_c) > len(c)): + ok = False + elif ( + # unmatched non-commutative bases + set(_[0] for _ in old_nc).difference(set(_[0] for _ in nc))): + ok = False + elif ( + # unmatched commutative terms + set(old_c).difference(set(c))): + ok = False + elif ( + # differences in sign + any(sign(c[b]) != sign(old_c[b]) for b in old_c)): + ok = False + if not ok: + return rv if not old_c: cdid = None @@ -1264,7 +1280,7 @@ def ndiv(a, b): c_e = c[b] rat.append(ndiv(c_e, old_e)) if not rat[-1]: - return fallback() + return rv cdid = min(rat) if not old_nc: @@ -1346,7 +1362,7 @@ def ndiv(a, b): else: if not ncdid: - return fallback() + return rv # although we didn't fail, certain nc terms may have # failed so we rebuild them after attempting a partial diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py index b1ef7bff010f..5f5ff8572a00 100644 --- a/sympy/core/numbers.py +++ b/sympy/core/numbers.py @@ -160,7 +160,6 @@ class Number(AtomicExpr): Rational(1) + sqrt(Rational(2)) """ is_commutative = True - is_comparable = True is_bounded = True is_finite = True is_number = True @@ -223,6 +222,9 @@ def __rdivmod__(self, other): raise TypeError(msg % (type(other).__name__, type(self).__name__)) return divmod(other, self) + def __round__(self, *args): + return round(float(self), *args) + def _as_mpf_val(self, prec): """Evaluation of mpf tuple accurate to at least prec bits.""" raise NotImplementedError('%s needs ._as_mpf_val() method' % \ @@ -245,6 +247,11 @@ def _eval_order(self, *symbols): # Order(5, x, y) -> Order(1,x,y) return C.Order(S.One, *symbols) + def _eval_subs(self, old, new): + if old == -self: + return -new + return self # there is no other possibility + @classmethod def class_key(cls): return 1, 0, 'Number' @@ -790,7 +797,7 @@ def __lt__(self, other): return False # sympy > other if isinstance(other, NumberSymbol): return other.__ge__(self) - if other.is_comparable: + if other.is_real and other.is_number: other = other.evalf() if isinstance(other, Number): return bool(mlib.mpf_lt(self._mpf_, other._as_mpf_val(self._prec))) @@ -803,7 +810,7 @@ def __le__(self, other): return False # sympy > other --> ! <= if isinstance(other, NumberSymbol): return other.__gt__(self) - if other.is_comparable: + if other.is_real and other.is_number: other = other.evalf() if isinstance(other, Number): return bool(mlib.mpf_le(self._mpf_, other._as_mpf_val(self._prec))) @@ -812,9 +819,6 @@ def __le__(self, other): def __hash__(self): return super(Float, self).__hash__() - def __round__(self, *args): - return round(float(self), *args) - def epsilon_eq(self, other, epsilon="10e-16"): return abs(self - other) < Float(epsilon) @@ -1152,7 +1156,7 @@ def __gt__(self, other): return False # sympy > other --> not < if isinstance(other, NumberSymbol): return other.__le__(self) - if other.is_comparable and not isinstance(other, Rational): + if other.is_real and other.is_number and not isinstance(other, Rational): other = other.evalf() if isinstance(other, Number): if isinstance(other, Float): @@ -1168,7 +1172,7 @@ def __ge__(self, other): return False # sympy > other --> not <= if isinstance(other, NumberSymbol): return other.__lt__(self) - if other.is_comparable and not isinstance(other, Rational): + if other.is_real and other.is_number and not isinstance(other, Rational): other = other.evalf() if isinstance(other, Number): if isinstance(other, Float): @@ -1185,7 +1189,7 @@ def __lt__(self, other): return False # sympy > other --> not < if isinstance(other, NumberSymbol): return other.__ge__(self) - if other.is_comparable and not isinstance(other, Rational): + if other.is_real and other.is_number and not isinstance(other, Rational): other = other.evalf() if isinstance(other, Number): if isinstance(other, Float): @@ -1201,7 +1205,7 @@ def __le__(self, other): return False # sympy > other --> not <= if isinstance(other, NumberSymbol): return other.__gt__(self) - if other.is_comparable and not isinstance(other, Rational): + if other.is_real and other.is_number and not isinstance(other, Rational): other = other.evalf() if isinstance(other, Number): if isinstance(other, Float): @@ -2213,16 +2217,15 @@ class NaN(Number): __metaclass__ = Singleton is_commutative = True - is_real = None - is_rational = None - is_integer = None - is_comparable = False - is_finite = None - is_bounded = None - #is_unbounded = False - is_zero = None - is_prime = None - is_positive = None + is_real = None + is_rational = None + is_integer = None + is_comparable = False + is_finite = None + is_bounded = None + is_zero = None + is_prime = None + is_positive = None __slots__ = [] @@ -2281,10 +2284,9 @@ class ComplexInfinity(AtomicExpr): __metaclass__ = Singleton is_commutative = True - is_comparable = None - is_bounded = False - is_real = None - is_number = True + is_bounded = False + is_real = None + is_number = False __slots__ = [] @@ -2318,7 +2320,6 @@ class NumberSymbol(AtomicExpr): __metaclass__ = Singleton is_commutative = True - is_comparable = True is_bounded = True is_finite = True is_number = True @@ -2370,7 +2371,7 @@ def __lt__(self, other): if other > u: return True return self.evalf() < other - if other.is_comparable: + if other.is_real and other.is_number: other = other.evalf() return self.evalf() < other return Expr.__lt__(self, other) @@ -2382,7 +2383,7 @@ def __le__(self, other): return False # sympy > other --> not <= if self is other: return True - if other.is_comparable: + if other.is_real and other.is_number: other = other.evalf() if isinstance(other, Number): return self.evalf() <= other diff --git a/sympy/core/power.py b/sympy/core/power.py index a513c2714f4e..4e360ce8e515 100644 --- a/sympy/core/power.py +++ b/sympy/core/power.py @@ -106,27 +106,22 @@ def class_key(cls): return 3, 2, cls.__name__ def _eval_power(self, other): + from sympy.functions.elementary.exponential import log + b, e = self.as_base_exp() - ##could process e.is_integer here; for now it's in powdenest - #if e.is_integer: - # return Pow(b, e * other) - if other.is_integer: - return Pow(b, e * other) - if b.is_nonnegative and (e.is_real or other.is_real): - return Pow(b, e * other) - if e.is_even and b.is_real: # hence b is pos and e is real - return Pow(abs(b), e * other) - if abs(e) < S.One and other.is_real: - return Pow(b, e * other) - if b.is_polar: - return Pow(b, e * other) - - def _eval_is_comparable(self): - c1 = self.base.is_comparable - if c1 is None: return - c2 = self.exp.is_comparable - if c2 is None: return - return c1 and c2 + b_nneg = b.is_nonnegative + if b.is_real and not b_nneg and e.is_even: + b = abs(b) + b_nneg = True + smallarg = (abs(e) <= abs(S.Pi/log(b))) + if (other.is_Rational and other.q == 2 and + e.is_real is False and smallarg is False): + return -Pow(b, e*other) + if (other.is_integer or + e.is_real and (b_nneg or abs(e) < 1) or + e.is_real is False and smallarg is True or + b.is_polar): + return Pow(b, e*other) def _eval_is_even(self): if self.exp.is_integer and self.exp.is_positive: @@ -241,8 +236,6 @@ def _eval_is_polar(self): return self.base.is_polar def _eval_subs(self, old, new): - if self == old: - return new if old.func is self.func and self.base == old.base: coeff1, terms1 = self.exp.as_coeff_Mul() coeff2, terms2 = old.exp.as_coeff_Mul() @@ -260,7 +253,6 @@ def _eval_subs(self, old, new): pow = coeff1/coeff2 if pow == int(pow) or self.base.is_positive: return Pow(new, pow) # (2**x).subs(exp(x*log(2)), z) -> z - return Pow(self.base._eval_subs(old, new), self.exp._eval_subs(old, new)) def as_base_exp(self): """Return base and exp of self unless base is 1/Integer, then return Integer, -exp. @@ -686,33 +678,30 @@ def as_numer_denom(self): return self, S.One base, exp = self.as_base_exp() n, d = base.as_numer_denom() - if d is not S.One: - if d.is_negative and n.is_negative: - n, d = -n, -d - if exp.is_Integer: - if exp.is_negative: - n, d = d, n - exp = -exp - return Pow(n, exp), Pow(d, exp) - elif exp.is_Rational or d.is_positive: - dneg = d.is_negative - if dneg is not None: - if dneg is True: - n = -n - d = -d - elif dneg is False: - n, d = d, n - exp = -exp - if _coeff_isneg(exp): - n, d = d, n - exp = -exp - return Pow(n, exp), Pow(d, exp) - # else we won't split up base but we check for neg expo below - if _coeff_isneg(exp): - return S.One, base**-exp - # unprocessed float or NumberSymbol exponent - # and Mul exp w/o negative sign - return self, S.One + # this should be the same as ExpBase.as_numer_denom wrt + # exponent handling + neg_exp = exp.is_negative + int_exp = exp.is_integer + if not neg_exp and not exp.is_real: + neg_exp = _coeff_isneg(exp) + # the denominator cannot be separated from the numerator if + # its sign is unknown unless the exponent is an integer, e.g. + # sqrt(a/b) != sqrt(a)/sqrt(b) when a=1 and b=-1. But if the + # denominator is negative the numerator and denominator can + # be negated and the denominator (now positive) separated. + if not (d.is_real or int_exp): + n = base + d = S.One + dnonpos = d.is_nonpositive + if dnonpos: + n, d = -n, -d + elif dnonpos is None and not int_exp: + n = base + d = S.One + if neg_exp: + n, d = d, n + exp = -exp + return Pow(n, exp), Pow(d, exp) def matches(self, expr, repl_dict={}): expr = _sympify(expr) diff --git a/sympy/core/relational.py b/sympy/core/relational.py index 5a3177e0bc46..9e0ea3d4e454 100644 --- a/sympy/core/relational.py +++ b/sympy/core/relational.py @@ -171,11 +171,6 @@ def lhs(self): def rhs(self): return self._args[1] - def _eval_subs(self, old, new): - if self == old: - return new - return self.__class__(self.lhs._eval_subs(old, new), self.rhs._eval_subs(old, new)) - def _eval_evalf(self, prec): return self.func(*[s._evalf(prec) for s in self.args]) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index 01a09fe0b6bb..af6efb6be9bf 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -1,30 +1,44 @@ -from basic import Basic -from singleton import Singleton, S -from evalf import EvalfMixin -from numbers import Float -from sympify import _sympify, sympify, SympifyError -from sympy.mpmath import mpi, mpf -from containers import Tuple +from sympy.core.sympify import _sympify, sympify, SympifyError +from sympy.core.basic import Basic +from sympy.core.singleton import Singleton, S +from sympy.core.evalf import EvalfMixin +from sympy.core.numbers import Float +from sympy.core.containers import Tuple +from sympy.mpmath import mpi, mpf +from sympy.assumptions import ask +from sympy.logic.boolalg import And, Or class Set(Basic): """ - The base class for any kind of set. This is not meant to be used directly - as a container of items. It does not behave like the builtin set; see - FiniteSet for that. + The base class for any kind of set. + + This is not meant to be used directly as a container of items. + It does not behave like the builtin set; see FiniteSet for that. Real intervals are represented by the Interval class and unions of sets by the Union class. The empty set is represented by the EmptySet class and available as a singleton as S.EmptySet. """ + is_number = False + is_iterable = False + is_interval = False + + is_FiniteSet = False + is_Interval = False + is_ProductSet = False + is_Union = False + is_Intersection = None + is_EmptySet = None + is_UniversalSet = None def union(self, other): """ - Returns the union of 'self' and 'other'. As a shortcut it is possible - to use the '+' operator: + Returns the union of 'self' and 'other'. - >>> from sympy import Interval, FiniteSet + As a shortcut it is possible to use the '+' operator: + >>> from sympy import Interval, FiniteSet >>> Interval(0, 1).union(Interval(2, 3)) [0, 1] U [2, 3] >>> Interval(0, 1) + Interval(2, 3) @@ -32,8 +46,7 @@ def union(self, other): >>> Interval(1, 2, True, True) + FiniteSet(2, 3) (1, 2] U {3} - Similarly it is possible to use the '-' operator for set - differences: + Similarly it is possible to use the '-' operator for set differences: >>> Interval(0, 2) - Interval(0, 1) (1, 2] @@ -53,10 +66,21 @@ def intersect(self, other): [1, 2] """ - return self._intersect(other) + return Intersection(self, other) def _intersect(self, other): - raise NotImplementedError("(%s)._intersect(%s)" % (self, other)) + """ + This function should only be used internally + + self._intersect(other) returns a new, intersected set if self knows how + to intersect itself with other, otherwise it returns None + + When making a new set class you can be assured that other will not + be a Union, FiniteSet, or EmptySet + + Used within the Intersection class + """ + return None @property def complement(self): @@ -84,7 +108,7 @@ def _complement(self): @property def inf(self): """ - The infimum of 'self'. + The infimum of 'self' >>> from sympy import Interval, Union @@ -102,7 +126,8 @@ def _inf(self): @property def sup(self): - """ The supremum of 'self'. + """ + The supremum of 'self' >>> from sympy import Interval, Union @@ -132,7 +157,7 @@ def contains(self, other): True """ - return self._contains(other) + return self._contains(sympify(other, strict=True)) def _contains(self, other): raise NotImplementedError("(%s)._contains(%s)" % (self, other)) @@ -157,7 +182,7 @@ def subset(self, other): @property def measure(self): """ - The (Lebesgue) measure of 'self'. + The (Lebesgue) measure of 'self' >>> from sympy import Interval, Union @@ -200,58 +225,24 @@ def __invert__(self): return self.complement def __contains__(self, other): - result = self.contains(other) - if not isinstance(result, bool): - raise TypeError('contains did not evaluate to a bool: %r' % result) + symb = self.contains(other) + result = ask(symb) + if result is None: + raise TypeError('contains did not evaluate to a bool: %r' % symb) return result - def _eval_subs(self, old, new): - if self == old: - return new - new_args = [] - for arg in self.args: - if arg == old: - new_args.append(new) - elif isinstance(arg, Basic): - new_args.append(arg._eval_subs(old, new)) - else: - new_args.append(arg) - return self.__class__(*new_args) - - @property - def is_number(self): - return False @property def is_real(self): return False - @property - def is_iterable(self): - return False - @property - def is_interval(self): - return False - @property - def is_FiniteSet(self): - return False - @property - def is_Interval(self): - return False - @property - def is_ProductSet(self): - return False - @property - def is_Union(self): - return False class ProductSet(Set): """ Represents a Cartesian Product of Sets. - Usage: - Returns a cartesian product given several sets as either an iterable - or individual arguments. + Returns a cartesian product given several sets as either an iterable + or individual arguments. - Can use '*' operator on any sets for convenient shorthand. + Can use '*' operator on any sets for convenient shorthand. Examples ======== @@ -276,10 +267,17 @@ class ProductSet(Set): (T, T) - Notes: - - Passes most operations down to the argument sets - - Flattens Products of ProductSets + Notes + ===== + - Passes most operations down to the argument sets + - Flattens Products of ProductSets + + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Cartesian_product """ + is_ProductSet = True def __new__(cls, *sets, **assumptions): def flatten(arg): @@ -300,7 +298,7 @@ def flatten(arg): def _contains(self, element): """ - in operator for ProductSets + 'in' operator for ProductSets >>> from sympy import Interval @@ -312,20 +310,18 @@ def _contains(self, element): Passes operation on to constitent sets """ - - if len(element) != len(self.args): + try: + if len(element) != len(self.args): + return False + except TypeError: # maybe element isn't an iterable return False - from sympy.logic.boolalg import And - return And(*[set.contains(item) for set,item in zip(self.sets,element)]) + return And(*[set.contains(item) for set, item in zip(self.sets, element)]) def _intersect(self, other): - if other.is_Union: - return Union(self.intersect(set) for set in other.args) if not other.is_ProductSet: - raise TypeError("%s is not a Product Set."%str(other)) + return None if len(other.args) != len(self.args): - raise ValueError("Sets not the same size Left: %d, Right: %d" - %(len(self.args), len(other.args))) + return S.EmptySet return ProductSet(a.intersect(b) for a, b in zip(self.sets, other.sets)) @@ -339,13 +335,11 @@ def _complement(self): # We need at least one of the sets to be complemented # Consider all 2^n combinations. # We can conveniently represent these options easily using a ProductSet - switch_sets = ProductSet(FiniteSet(set, set.complement) - for set in self.sets) + switch_sets = ProductSet(FiniteSet(s, s.complement) for s in self.sets) product_sets = (ProductSet(*set) for set in switch_sets) # Union of all combinations but this one return Union(p for p in product_sets if p != self) - @property def is_real(self): return all(set.is_real for set in self.sets) @@ -368,30 +362,22 @@ def _measure(self): measure *= set.measure return measure - @property - def is_ProductSet(self): - return True - class RealSet(Set, EvalfMixin): """ A set of real values """ - @property - def is_real(self): - return True + is_real = True class CountableSet(Set): """ Represents a set of countable numbers such as {1, 2, 3, 4} or {1, 2, 3, ...} """ + is_iterable = True + @property def _measure(self): return 0 - @property - def is_iterable(self): - return True - def __iter__(self): raise NotImplementedError("Iteration not yet implemented") @@ -420,12 +406,19 @@ class Interval(RealSet): >>> Interval(0, a) [0, a] - Notes: - - Only real end points are supported - - Interval(a, b) with a > b will return the empty set - - Use the evalf() method to turn an Interval into an mpmath - 'mpi' interval instance + Notes + ===== + - Only real end points are supported + - Interval(a, b) with a > b will return the empty set + - Use the evalf() method to turn an Interval into an mpmath + 'mpi' interval instance + + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Interval_(mathematics) """ + is_Interval = True def __new__(cls, start, end, left_open=False, right_open=False): @@ -457,8 +450,9 @@ def __new__(cls, start, end, left_open=False, right_open=False): @property def start(self): """ - The left end point of 'self'. This property takes the same value as the - 'inf' property. + The left end point of 'self'. + + This property takes the same value as the 'inf' property. >>> from sympy import Interval @@ -473,8 +467,9 @@ def start(self): @property def end(self): """ - The right end point of 'self'. This property takes the same value as the - 'sup' property. + The right end point of 'self'. + + This property takes the same value as the 'sup' property. >>> from sympy import Interval @@ -517,12 +512,12 @@ def right_open(self): return self._args[3] def _intersect(self, other): - if not isinstance(other, Interval): - return other.intersect(self) - + # We only know how to intersect with other intervals + if not other.is_Interval: + return None + # We can't intersect [0,3] with [x,6] -- we don't know if x>0 or x<0 if not self._is_comparable(other): - raise NotImplementedError("Intersection of intervals with symbolic " - "end points is not yet implemented") + return None empty = False @@ -565,14 +560,6 @@ def _complement(self): return Union(a, b) def _contains(self, other): - # We use the logic module here so that this method is meaningful - # when used with symbolic end points. - from sympy.logic.boolalg import And - try: - other = _sympify(other) - except SympifyError: - return False - if self.left_open: expr = other > self.start else: @@ -603,9 +590,6 @@ def _is_comparable(self, other): is_comparable &= other.end.is_comparable return is_comparable - @property - def is_Interval(self): - return True @property def is_left_unbounded(self): @@ -620,7 +604,6 @@ def is_right_unbounded(self): def as_relational(self, symbol): """Rewrite an interval in terms of inequalities and logic operators. """ from sympy.core.relational import Lt, Le - from sympy.logic.boolalg import And if not self.is_left_unbounded: if self.left_open: @@ -660,10 +643,18 @@ class Union(Set): >>> Union(Interval(1, 2), Interval(2, 3)) [1, 3] + See Also + ======== + Intersection + + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Union_(set_theory) """ + is_Union = True def __new__(cls, *args): - # Flatten out Iterators and Unions to form one list of sets args = list(args) def flatten(arg): @@ -736,26 +727,6 @@ def _sup(self): from sympy.functions.elementary.miscellaneous import Max return Max(*[set.sup for set in self.args]) - def _intersect(self, other): - # Distributivity. - if other.is_Interval: - intersections = [] - for interval in self.args: - intersections.append(interval.intersect(other)) - return self.__class__(*intersections) - - if other.is_FiniteSet: - return other._intersect(self) - - elif other.is_Union: - intersections = [] - for s in other.args: - intersections.append(self.intersect(s)) - return self.__class__(*intersections) - - else: - return other.intersect(self) - @property def _complement(self): # De Morgan's formula. @@ -765,7 +736,6 @@ def _complement(self): return complement def _contains(self, other): - from sympy.logic.boolalg import Or or_args = [the_set.contains(other) for the_set in self.args] return Or(*or_args) @@ -814,18 +784,153 @@ def _measure(self): return measure def as_relational(self, symbol): - """Rewrite a Union in terms of equalities and logic operators. - """ - from sympy.logic.boolalg import Or + """Rewrite a Union in terms of equalities and logic operators. """ return Or(*[set.as_relational(symbol) for set in self.args]) @property def is_iterable(self): return all(arg.is_iterable for arg in self.args) + +class Intersection(Set): + """ + Represents an untersection of sets as a Set. + + Examples + ======== + + >>> from sympy import Intersection, Interval + + >>> Intersection(Interval(1, 3), Interval(2, 4)) + [2, 3] + + We often use the .intersect method + + >>> Interval(1,3).intersect(Interval(2,4)) + [2, 3] + + See Also + ======== + Union + + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Intersection_(set_theory) + """ + is_Intersection = True + + def __new__(cls, *args, **kwargs): + evaluate = kwargs.get('evaluate', True) + + # flatten inputs to merge intersections and iterables + args = list(args) + def flatten(arg): + if isinstance(arg, Set): + if arg.is_Intersection: + return sum(map(flatten, arg.args), []) + else: + return [arg] + if is_flattenable(arg): # and not isinstance(arg, Set) (implicit) + return sum(map(flatten, arg), []) + raise TypeError("Input must be Sets or iterables of Sets") + args = flatten(args) + + # Intersection of no sets is everything + if len(args)==0: + return S.UniversalSet + + # Reduce sets using known rules + if evaluate: + return Intersection.reduce(args) + + return Basic.__new__(cls, *args) + @property - def is_Union(self): - return True + def is_iterable(self): + return any(arg.is_iterable for arg in self.args) + + @property + def _inf(self): + raise NotImplementedError() + + @property + def _sup(self): + raise NotImplementedError() + + @property + def _complement(self): + raise NotImplementedError() + + def _contains(self, other): + from sympy.logic.boolalg import And + return And(*[set.contains(other) for set in self.args]) + + def __iter__(self): + for s in self.args: + if s.is_iterable: + other_sets = set(self.args) - set((s,)) + other = Intersection(other_sets, evaluate=False) + return (x for x in s if x in other) + + raise ValueError("None of the constituent sets are iterable") + + @staticmethod + def reduce(args): + """ + Simplify an intersection using known rules + + We first start with global rules like + 'if any empty sets return empty set' and 'distribute any unions' + + Then we iterate through all pairs and ask the constituent sets if they + can simplify themselves with any other constituent + """ + + # ===== Global Rules ===== + # If any EmptySets return EmptySet + if any(s.is_EmptySet for s in args): + return S.EmptySet + + # If any FiniteSets see which elements of that finite set occur within + # all other sets in the intersection + for s in args: + if s.is_FiniteSet: + return s.__class__(x for x in s + if all(x in other for other in args)) + + # If any of the sets are unions, return a Union of Intersections + for s in args: + if s.is_Union: + other_sets = set(args) - set((s,)) + other = Intersection(other_sets) + return Union(Intersection(arg, other) for arg in s.args) + + # At this stage we are guaranteed not to have any + # EmptySets, FiniteSets, or Unions in the intersection + + # ===== Pair-wise Rules ===== + # Here we depend on rules built into the constituent sets + args = set(args) + new_args = True + while(new_args): + for s in args: + new_args = False + for t in args - set((s,)): + new_set = s._intersect(t) + # This returns None if s does not know how to intersect + # with t. Returns the newly intersected set otherwise + if new_set is not None: + new_args = (args - set((s, t))).union(set((new_set, ))) + break + if new_args: + args = new_args + break + + if len(args)==1: + return args.pop() + else: + return Intersection(args, evaluate=False) class RealUnion(Union, RealSet): """ @@ -958,16 +1063,24 @@ class EmptySet(Set): >>> Interval(1, 2).intersect(S.EmptySet) EmptySet() - """ + See Also + ======== + UniversalSet + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Empty_set + """ __metaclass__ = Singleton + is_EmptySet = True def _intersect(self, other): return S.EmptySet @property def _complement(self): - return Interval(S.NegativeInfinity, S.Infinity) + return S.UniversalSet @property def _measure(self): @@ -988,6 +1101,55 @@ def union(self, other): def __iter__(self): return iter([]) +class UniversalSet(Set): + """ + Represents the set of all things. + The universal set is available as a singleton as S.UniversalSet + + Examples + ======== + + >>> from sympy import S, Interval + + >>> S.UniversalSet + UniversalSet() + + >>> Interval(1, 2).intersect(S.UniversalSet) + [1, 2] + + See Also + ======== + EmptySet + + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Universal_set + """ + + __metaclass__ = Singleton + is_UniversalSet = True + + def _intersect(self, other): + return other + + @property + def _complement(self): + return S.EmptySet + + @property + def _measure(self): + return S.Infinity + + def _contains(self, other): + return True + + def as_relational(self, symbol): + return True + + def union(self, other): + return self + class FiniteSet(CountableSet): """ Represents a finite set of discrete numbers @@ -1002,7 +1164,13 @@ class FiniteSet(CountableSet): >>> 3 in FiniteSet(1, 2, 3, 4) True + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Finite_set """ + is_FiniteSet = True + def __new__(cls, *args): def flatten(arg): if is_flattenable(arg): @@ -1010,10 +1178,7 @@ def flatten(arg): return [arg] args = flatten(list(args)) - # Sympify Arguments args = map(sympify, args) - # Turn tuples into Tuples - args = [Tuple(*arg) if arg.__class__ is tuple else arg for arg in args] if len(args) == 0: return EmptySet() @@ -1024,7 +1189,7 @@ def flatten(arg): except AttributeError: pass - elements = frozenset(map(sympify, args)) + elements = frozenset(args) obj = Basic.__new__(cls, elements) obj.elements = elements return obj @@ -1043,8 +1208,9 @@ def _intersect(self, other): def union(self, other): """ - Returns the union of 'self' and 'other'. As a shortcut it is possible - to use the '+' operator: + Returns the union of 'self' and 'other'. + + As a shortcut it is possible to use the '+' operator: >>> from sympy import FiniteSet, Interval, Symbol @@ -1063,9 +1229,7 @@ def union(self, other): >>> Interval(1, 2) - FiniteSet(2, 3) [1, 2) - """ - if other == S.EmptySet: return self if other.is_FiniteSet: @@ -1075,6 +1239,7 @@ def union(self, other): def _contains(self, other): """ Tests whether an element, other, is in the set. + Relies on Python's set class. This tests for object equality All inputs are sympified @@ -1086,7 +1251,7 @@ def _contains(self, other): False """ - return sympify(other) in self.elements + return other in self.elements @property def _inf(self): @@ -1105,16 +1270,10 @@ def __sub__(self, other): return FiniteSet(el for el in self if el not in other) def as_relational(self, symbol): - """Rewrite a FiniteSet in terms of equalities and logic operators. - """ + """Rewrite a FiniteSet in terms of equalities and logic operators. """ from sympy.core.relational import Eq - from sympy.logic.boolalg import Or return Or(*[Eq(symbol, elem) for elem in self]) - @property - def is_FiniteSet(self): - return True - @property def is_real(self): return all(el.is_real for el in self) @@ -1125,11 +1284,14 @@ def compare(self, other): class RealFiniteSet(FiniteSet, RealSet): """ A FiniteSet with all elements Real Numbers. - Allows for good integration with Intervals + + Allows for good integration with Intervals. This class for internal use only. Use FiniteSet to create a RealFiniteSet - See FiniteSet for more details + See Also + ======== + FiniteSet """ def _eval_evalf(self, prec): @@ -1160,10 +1322,8 @@ def _complement(self): return Union(*intervals) def as_relational(self, symbol): - """Rewrite a FiniteSet in terms of equalities and logic operators. - """ + """Rewrite a FiniteSet in terms of equalities and logic operators. """ from sympy.core.relational import Eq - from sympy.logic.boolalg import Or return Or(*[Eq(symbol, elem) for elem in self]) genclass = (1 for i in xrange(2)).__class__ diff --git a/sympy/core/sympify.py b/sympy/core/sympify.py index 54a792ffc1f8..8ecbbc228f4c 100644 --- a/sympy/core/sympify.py +++ b/sympy/core/sympify.py @@ -122,10 +122,6 @@ class or its derived classes. if strict: raise SympifyError(a) - if isinstance(a, tuple): - from containers import Tuple - return Tuple(*[sympify(x, locals=locals, convert_xor=convert_xor, - rational=rational) for x in a]) if iterable(a): try: return type(a)([sympify(x, locals=locals, convert_xor=convert_xor, diff --git a/sympy/core/tests/test_args.py b/sympy/core/tests/test_args.py index a9652f5ad57c..2360f5677b84 100644 --- a/sympy/core/tests/test_args.py +++ b/sympy/core/tests/test_args.py @@ -312,6 +312,10 @@ def test_sympy__core__sets__EmptySet(): from sympy.core.sets import EmptySet assert _test_args(EmptySet()) +def test_sympy__core__sets__UniversalSet(): + from sympy.core.sets import UniversalSet + assert _test_args(UniversalSet()) + def test_sympy__core__sets__FiniteSet(): from sympy.core.sets import FiniteSet assert _test_args(FiniteSet(x, y, z)) @@ -343,6 +347,11 @@ def test_sympy__core__sets__Set(): from sympy.core.sets import Set assert _test_args(Set()) +def test_sympy__core__sets__Intersection(): + from sympy.core.sets import Intersection, Interval + assert _test_args(Intersection(Interval(0, 3), Interval(2, 4), + evaluate=False)) + def test_sympy__core__sets__Union(): from sympy.core.sets import Union, Interval assert _test_args(Union(Interval(0, 1), Interval(2, 3))) diff --git a/sympy/core/tests/test_arit.py b/sympy/core/tests/test_arit.py index 3ef74238af65..7ce8b1f4cfa9 100644 --- a/sympy/core/tests/test_arit.py +++ b/sympy/core/tests/test_arit.py @@ -1089,6 +1089,7 @@ def test_Add_is_irrational(): assert (i+1).is_irrational == True assert (i+1).is_rational == False +@XFAIL def test_issue432(): class MightyNumeric(tuple): def __rdiv__(self, other): diff --git a/sympy/core/tests/test_assumptions.py b/sympy/core/tests/test_assumptions.py index 92741258e7b1..3f4895849695 100644 --- a/sympy/core/tests/test_assumptions.py +++ b/sympy/core/tests/test_assumptions.py @@ -8,6 +8,7 @@ def test_symbol_unset(): assert x.is_integer == True assert x.is_imaginary == False assert x.is_noninteger == False + assert x.is_number is False def test_zero(): z = Integer(0) @@ -32,6 +33,7 @@ def test_zero(): assert z.is_comparable == True assert z.is_prime == False assert z.is_composite == False + assert z.is_number is True def test_one(): z = Integer(1) @@ -55,6 +57,7 @@ def test_one(): assert z.is_infinitesimal == False assert z.is_comparable == True assert z.is_prime == False + assert z.is_number is True @XFAIL def test_one_is_composite(): @@ -83,6 +86,7 @@ def test_negativeone(): assert z.is_comparable == True assert z.is_prime == False assert z.is_composite == False + assert z.is_number is True def test_infinity(): oo = S.Infinity @@ -108,6 +112,7 @@ def test_infinity(): assert oo.is_comparable == True assert oo.is_prime == None assert oo.is_composite == None + assert oo.is_number is True def test_neg_infinity(): mm = S.NegativeInfinity @@ -133,6 +138,8 @@ def test_neg_infinity(): assert mm.is_comparable == True assert mm.is_prime == False assert mm.is_composite == False + assert mm.is_number is True + def test_nan(): nan = S.NaN @@ -158,6 +165,7 @@ def test_nan(): assert nan.is_comparable == False assert nan.is_prime == None assert nan.is_composite == None + assert nan.is_number is True def test_pos_rational(): r = Rational(3,4) @@ -271,27 +279,27 @@ def test_E(): def test_I(): z = S.ImaginaryUnit - assert z.is_commutative == True - assert z.is_integer == False - assert z.is_rational == False - assert z.is_real == False - assert z.is_complex == True - assert z.is_noninteger == False - assert z.is_irrational == False - assert z.is_imaginary == True - assert z.is_positive == False - assert z.is_negative == False - assert z.is_nonpositive == False - assert z.is_nonnegative == False - assert z.is_even == False - assert z.is_odd == False - assert z.is_bounded == True - assert z.is_unbounded == False - assert z.is_finite == True - assert z.is_infinitesimal == False - assert z.is_comparable == None - assert z.is_prime == False - assert z.is_composite == False + assert z.is_commutative is True + assert z.is_integer is False + assert z.is_rational is False + assert z.is_real is False + assert z.is_complex is True + assert z.is_noninteger is False + assert z.is_irrational is False + assert z.is_imaginary is True + assert z.is_positive is False + assert z.is_negative is False + assert z.is_nonpositive is False + assert z.is_nonnegative is False + assert z.is_even is False + assert z.is_odd is False + assert z.is_bounded is True + assert z.is_unbounded is False + assert z.is_finite is True + assert z.is_infinitesimal is False + assert z.is_comparable is False + assert z.is_prime is False + assert z.is_composite is False def test_symbol_real(): # issue 749 diff --git a/sympy/core/tests/test_basic.py b/sympy/core/tests/test_basic.py index bd508465f320..c12c7d06836f 100644 --- a/sympy/core/tests/test_basic.py +++ b/sympy/core/tests/test_basic.py @@ -55,8 +55,8 @@ def test_subs(): assert b21.subs({b1: b2, b2: b1}) == Basic(b2, b2) - raises(TypeError, "b21.subs('bad arg')") - raises(TypeError, "b21.subs(b1, b2, b3)") + raises(ValueError, "b21.subs('bad arg')") + raises(ValueError, "b21.subs(b1, b2, b3)") def test_atoms(): assert b21.atoms() == set() diff --git a/sympy/core/tests/test_eval_power.py b/sympy/core/tests/test_eval_power.py index 5c016bec9517..e3d8f6c8dd10 100644 --- a/sympy/core/tests/test_eval_power.py +++ b/sympy/core/tests/test_eval_power.py @@ -1,5 +1,7 @@ from sympy.core import Rational, Symbol, S, Float, Integer, Number, Pow, Basic from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.exponential import exp +from sympy.utilities.pytest import XFAIL def test_issue153(): #test that is runs: @@ -46,7 +48,7 @@ def test_issue350(): assert (a**2)**b == (abs(a)**b)**2 assert sqrt(1/a) != 1/sqrt(a) # e.g. for a = -1 assert (a**3)**Rational(1,3) != a - assert (x**a)**b != x**(a*b) # e.g. x = -1, a=1/2, b=2 + assert (x**a)**b != x**(a*b) # e.g. x = -1, a=2, b=1/2 assert (x**.5)**b == x**(.5*b) assert (x**.5)**.5 == x**.25 assert (x**2.5)**.5 != x**1.25 # e.g. for x = 5*I @@ -62,7 +64,7 @@ def test_issue350(): a = Symbol('a', positive=True) assert (a**3)**Rational(2,5) == a**Rational(6,5) assert (a**2)**b == (a**b)**2 - assert (a**Rational(2, 3))**x == (a**x)**Rational(2, 3) == (a**(2*x/3)) + assert (a**Rational(2, 3))**x == (a**(2*x/3)) != (a**x)**Rational(2, 3) def test_issue767(): assert --sqrt(sqrt(5)-1)==sqrt(sqrt(5)-1) @@ -74,7 +76,7 @@ def test_negative_one(): def test_issue1263(): neg = Symbol('neg', negative=True) - nonneg = Symbol('nonneg', negative=False) + nonneg = Symbol('nonneg', nonnegative=True) any = Symbol('any') num, den = sqrt(1/neg).as_numer_denom() assert num == sqrt(-1) @@ -117,7 +119,7 @@ def eqn(num, den, pow): eq=eqn(npos, dpos, pow) assert eq.is_Pow and eq.as_numer_denom() == (npos**pow, dpos**pow) eq=eqn(npos, dneg, pow) - assert eq.is_Pow and eq.as_numer_denom() == (eq, 1) + assert eq.is_Pow and eq.as_numer_denom() == ((-npos)**pow, (-dneg)**pow) eq=eqn(nneg, dpos, pow) assert eq.is_Pow and eq.as_numer_denom() == (nneg**pow, dpos**pow) eq=eqn(nneg, dneg, pow) @@ -125,16 +127,37 @@ def eqn(num, den, pow): eq=eqn(npos, dpos, -pow) assert eq.as_numer_denom() == (dpos**pow, npos**pow) eq=eqn(npos, dneg, -pow) - assert eq.is_Pow and eq.as_numer_denom() == (1, eq.base**pow) + assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-npos)**pow) eq=eqn(nneg, dpos, -pow) assert eq.is_Pow and eq.as_numer_denom() == (dpos**pow, nneg**pow) eq=eqn(nneg, dneg, -pow) assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-nneg)**pow) x = Symbol('x') + y = Symbol('y') assert ((1/(1 + x/3))**(-S.One)).as_numer_denom() == (3 + x, 3) - np = Symbol('np',positive=False) - assert (((1 + x/np)**-2)**(-S.One)).as_numer_denom() == ((np + x)**2, np**2) + notp = Symbol('notp', positive=False) # not positive does not imply real + b = ((1 + x/notp)**-2) + assert (b**(-y)).as_numer_denom() == (1, b**y) + assert (b**(-S.One)).as_numer_denom() == ((notp + x)**2, notp**2) + nonp = Symbol('nonp', nonpositive=True) + assert (((1 + x/nonp)**-2)**(-S.One)).as_numer_denom() == ((-nonp - x)**2, nonp**2) + + n = Symbol('n', negative=True) + assert (x**n).as_numer_denom() == (1, x**-n) + assert sqrt(1/n).as_numer_denom() == (S.ImaginaryUnit, sqrt(-n)) + n = Symbol('0 or neg', nonpositive=True) + # if x and n are split up without negating each term and n is negative + # then the answer might be wrong; if n is 0 it won't matter since + # 1/oo and 1/zoo are both zero as is sqrt(0)/sqrt(-x) unless x is also + # zero (in which case the negative sign doesn't matter): + # 1/sqrt(1/-1) = -I but sqrt(-1)/sqrt(1) = I + assert (1/sqrt(x/n)).as_numer_denom() == (sqrt(-n), sqrt(-x)) + c = Symbol('c', complex=True) + e = sqrt(1/c) + assert e.as_numer_denom() == (e, 1) + i = Symbol('i', integer=True) + assert (((1 + x/y)**i)).as_numer_denom() == ((x + y)**i, y**i) def test_Pow_signs(): """Cf. issues 1496 and 2151""" @@ -188,3 +211,19 @@ def test_issue_3001(): # __eq__ methods could be added to Symbol and Pow to detect the # power-of-1.0 case. assert ((x*y)**1.0).func is Pow + +def test_issue_3109(): + from sympy import root, Rational + I = S.ImaginaryUnit + assert sqrt(33**(9*I/10)) == -33**(9*I/20) + assert root((6*I)**(2*I), 3).as_base_exp()[1] == Rational(1, 3) # != 2*I/3 + assert root((6*I)**(I/3), 3).as_base_exp()[1] == I/9 + assert sqrt(exp(3*I)) == exp(3*I/2) + assert sqrt(-sqrt(3)*(1 + 2*I)) == sqrt(sqrt(3))*sqrt(-1 - 2*I) + +@XFAIL +def test_issue_3109_fail(): + from sympy import root, Rational + I = S.ImaginaryUnit + assert sqrt(exp(5*I)) == -exp(5*I/2) + assert root(exp(5*I), 3).exp == Rational(1, 3) diff --git a/sympy/core/tests/test_evalf.py b/sympy/core/tests/test_evalf.py index c6446d991ba4..9a79512506ab 100644 --- a/sympy/core/tests/test_evalf.py +++ b/sympy/core/tests/test_evalf.py @@ -285,7 +285,7 @@ def test_old_docstring(): assert a.n() == 17.25866050002001 def test_issue_1707(): - assert round(integrate(atan(x)**2, (x, -1, 1)).evalf(), 1) == 0.5 + assert integrate(atan(x)**2, (x, -1, 1)).evalf().round(1) == 0.5 assert atan(0, evaluate=False).n() == 0 def test_evalf_mul(): diff --git a/sympy/core/tests/test_expr.py b/sympy/core/tests/test_expr.py index 551a1f3630b2..45bfa40a2615 100644 --- a/sympy/core/tests/test_expr.py +++ b/sympy/core/tests/test_expr.py @@ -5,7 +5,7 @@ Poly, Function, Derivative, Number, pi, NumberSymbol, zoo, Piecewise, Mul, Pow, nsimplify, ratsimp, trigsimp, radsimp, powsimp, simplify, together, separate, collect, factorial, apart, combsimp, factor, refine, cancel, - Tuple, default_sort_key, DiracDelta, gamma, Dummy, Sum) + Tuple, default_sort_key, DiracDelta, gamma, Dummy, Sum, E) from sympy.core.function import AppliedUndef from sympy.abc import a, b, c, d, e, n, t, u, x, y, z from sympy.physics.secondquant import FockState @@ -419,11 +419,6 @@ def test_as_numer_denom(): assert ((x**2+1)/y).as_numer_denom() == (x**2+1, y) assert (x*(y+1)/y**7).as_numer_denom() == (x*(y+1), y**7) assert (x**-2).as_numer_denom() == (1, x**2) - n = symbols('n', negative=True) - assert (x**n).as_numer_denom() == (x**n, 1) - assert sqrt(1/n).as_numer_denom() == (I, sqrt(-n)) - n = Symbol('0 or neg', nonpositive=True) - assert (1/sqrt(x/n)).as_numer_denom() == (1, sqrt(x/n)) assert (a/x + b/2/x + c/3/x).as_numer_denom() == \ (6*a + 3*b + 2*c, 6*x) assert (a/x + b/2/x + c/3/y).as_numer_denom() == \ @@ -497,25 +492,6 @@ def test_as_independent(): assert (x + Integral(x, (x, 1, 2))).as_independent(x, strict=True) == \ (Integral(x, (x, 1, 2)), x) -def test_subs_dict(): - assert (sin(x))._subs_dict({ x : 1, sin(x) : 2}) == 2 - assert (sin(x))._subs_dict([(x, 1), (sin(x), 2)]) == 2 - - expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) - - seq = [ (sqrt(sin(2*x)),a), (cos(2*x),b), (sin(2*x),c), (x,d), (exp(x),e) ] - assert expr._subs_dict(seq) == c + a*b*sin(d*e) - - seq = [ (sqrt(sin(2*x)),a), (sin(2*x),c), (cos(2*x),b), (x,d), (exp(x),e) ] - assert expr._subs_dict(seq) == c + a*b*sin(d*e) - -def test_subs_list(): - assert (sin(x))._subs_list([(sin(x), 2), (x, 1)]) == 2 - assert (sin(x))._subs_list([(x, 1), (sin(x), 2)]) == sin(1) - - assert (x+y)._subs_list([(x, 3), (y, x**2)]) == 3 + x**2 - assert (x+y)._subs_list([(y, x**2), (x, 3)]) == 12 - def test_call(): # Unlike what used to be the case, the following should NOT work. # See issue 1927. @@ -1300,3 +1276,77 @@ def test_equals(): def test_random(): from sympy import posify assert posify(x)[0]._random() is not None + +def test_round(): + from sympy.abc import x + + assert Float('0.1249999').round(2) == 0.12 + d20 = 12345678901234567890 + ans = S(d20).round(2) + assert ans.is_Float and ans == d20 + ans = S(d20).round(-2) + assert ans.is_Float and ans == 12345678901234567900 + assert S('1/7').round(4) == 0.1429 + assert S('.[12345]').round(4) == 0.1235 + assert S('.1349').round(2) == 0.13 + n = S(12345) + ans = n.round() + assert ans.is_Float + assert ans == n + ans = n.round(1) + assert ans.is_Float + assert ans == n + ans = n.round(4) + assert ans.is_Float + assert ans == n + assert n.round(-1) == 12350 + + r = n.round(-4) + assert r == 10000 + # in fact, it should equal many values since __eq__ + # compares at equal precision + assert all(r == i for i in range(9984, 10049)) + + assert n.round(-5) == 0 + + assert (pi + sqrt(2)).round(2) == 4.56 + assert (10*(pi + sqrt(2))).round(-1) == 50 + raises(TypeError, 'round(x + 2, 2)') + assert S(2.3).round(1) == 2.3 + e = S(12.345).round(2) + assert e == round(12.345, 2) + assert type(e) is Float + + assert (Float(.3, 3) + 2*pi).round() == 7 + assert (Float(.3, 3) + 2*pi*100).round() == 629 + assert (Float(.03, 3) + 2*pi/100).round(5) == 0.09283 + assert (Float(.03, 3) + 2*pi/100).round(4) == 0.0928 + assert (pi + 2*E*I).round() == 3 + 5*I + + assert S.Zero.round() == 0 + + a = (Add(1, Float('1.'+'9'*27, ''), evaluate=0)) + assert a.round(10) == Float('3.0000000000','') + assert a.round(25) == Float('3.0000000000000000000000000','') + assert a.round(26) == Float('3.00000000000000000000000000','') + assert a.round(27) == Float('2.999999999999999999999999999','') + assert a.round(30) == Float('2.999999999999999999999999999','') + + raises(TypeError, 'x.round()') + + # exact magnitude of 10 + assert str(S(1).round()) == '1.' + assert str(S(100).round()) == '100.' + + # applied to real and imaginary portions + assert (2*pi + E*I).round() == 6 + 3*I + assert (2*pi + I/10).round() == 6 + assert (pi/10 + 2*I).round() == 2*I + # the lhs re and im parts are Float with dps of 2 + # and those on the right have dps of 15 so they won't compare + # equal unless we use string or compare components (which will + # then coerce the floats to the same precision) or re-create + # the floats + assert str((pi/10 + E*I).round(2)) == '0.31 + 2.72*I' + assert (pi/10 + E*I).round(2).as_real_imag() == (0.31, 2.72) + assert (pi/10 + E*I).round(2) == Float(0.31, 2) + I*Float(2.72, 3) diff --git a/sympy/core/tests/test_numbers.py b/sympy/core/tests/test_numbers.py index 7f5bd0fb134a..92ede1943f97 100644 --- a/sympy/core/tests/test_numbers.py +++ b/sympy/core/tests/test_numbers.py @@ -1,6 +1,6 @@ from sympy import (Rational, Symbol, Float, I, sqrt, oo, nan, pi, E, Integer, S, factorial, Catalan, EulerGamma, GoldenRatio, cos, exp, - Number, zoo, log, Mul, Pow, Tuple, round) + Number, zoo, log, Mul, Pow, Tuple) from sympy.core.power import integer_nthroot from sympy.utilities.pytest import XFAIL, slow @@ -55,9 +55,9 @@ def test_mod(): a = Float(2.6) - assert round(a % .2, 15) == 0.2 - assert round(a % 2, 15) == 0.6 - assert round(a % 0.5, 15) == 0.1 + assert (a % .2).round(15) == 0.2 + assert (a % 2).round(15) == 0.6 + assert (a % 0.5).round(15) == 0.1 # No rounding required since these numbers can be represented # exactly. @@ -1142,9 +1142,9 @@ def test_hashing_sympy_integers(): assert hash(S(4)) == hash(int(4)) def test_issue_1073(): - assert int(round(E**100)) == 26881171418161354484126255515800135873611119 - assert int(round(pi**100)) == 51878483143196131920862615246303013562686760680406 - assert int(round(Rational(1)/EulerGamma**100)) == 734833795660954410469466 + assert int((E**100).round()) == 26881171418161354484126255515800135873611119 + assert int((pi**100).round()) == 51878483143196131920862615246303013562686760680406 + assert int((Rational(1)/EulerGamma**100).round()) == 734833795660954410469466 @XFAIL def test_mpmath_issues(): diff --git a/sympy/core/tests/test_sets.py b/sympy/core/tests/test_sets.py index 1feace034032..8517799be072 100644 --- a/sympy/core/tests/test_sets.py +++ b/sympy/core/tests/test_sets.py @@ -1,7 +1,7 @@ from sympy import ( Symbol, Set, Union, Interval, oo, S, sympify, nan, GreaterThan, LessThan, Max, Min, And, Or, Eq, Ge, Le, Gt, Lt, Float, - FiniteSet + FiniteSet, Intersection ) from sympy.mpmath import mpi @@ -75,7 +75,6 @@ def test_union(): assert FiniteSet(1,2,3) & FiniteSet(2,3,4) == FiniteSet(2,3) assert FiniteSet(1,2,3) | FiniteSet(2,3,4) == FiniteSet(1,2,3,4) - # Test that Intervals and FiniteSets play nicely assert Interval(1,3) + FiniteSet(2) == Interval(1,3) assert Interval(1,3, True,True) + FiniteSet(3) == Interval(1,3, True,False) @@ -85,9 +84,10 @@ def test_union(): assert 2 in X and 3 in X and 3 in XandY assert X.subset(XandY) and Y.subset(XandY) - raises(TypeError, "Union(1, 2, 3)") + assert X.is_iterable == False + def test_difference(): assert Interval(1, 3) - Interval(1, 2) == Interval(2, 3, True) assert Interval(1, 3) - Interval(2, 3) == Interval(1, 2, False, True) @@ -117,7 +117,8 @@ def test_complement(): assert -S.EmptySet == S.EmptySet.complement assert ~S.EmptySet == S.EmptySet.complement - assert S.EmptySet.complement == Interval(-oo, oo) + assert S.EmptySet.complement == S.UniversalSet + assert S.UniversalSet.complement == S.EmptySet assert Union(Interval(0, 1), Interval(2, 3)).complement == \ Union(Interval(-oo, 0, True, True), Interval(1, 2, True, True), @@ -142,7 +143,6 @@ def test_complement(): assert not any(pt in square for pt in [(-1,0), (1.5,.5), (10,10)]) assert all(pt in notsquare for pt in [(-1,0), (1.5,.5), (10,10)]) - def test_intersect(): x = Symbol('x') assert Interval(0, 2).intersect(Interval(1, 2)) == Interval(1, 2) @@ -174,6 +174,29 @@ def test_intersect(): assert Union(Interval(0,5), FiniteSet(['Ham'])).intersect(FiniteSet(2,3,4,5,6)) == \ FiniteSet(2,3,4,5) +def test_intersection(): + # iterable + i = Intersection(FiniteSet(1,2,3), Interval(2, 5), evaluate=False) + assert i.is_iterable + assert list(i) == [2, 3] + + # challenging intervals + x = Symbol('x', real=True) + i = Intersection(Interval(0, 3), Interval(x, 6)) + assert (5 in i) == False + raises(TypeError, "2 in i") + + # Singleton special cases + assert Intersection(Interval(0, 1), S.EmptySet) == S.EmptySet + assert Intersection(Interval(0, 1), S.UniversalSet) == Interval(0, 1) + + # Products + line = Interval(0, 5) + i = Intersection(line**2, line**3, evaluate=False) + assert (2,2) not in i + assert (2,2,2) not in i + raises(ValueError, "list(i)") + def test_interval_subs(): a = Symbol('a', real=True) @@ -244,9 +267,11 @@ def test_contains(): assert FiniteSet(1,2,3).contains(2) assert FiniteSet(1,2,Symbol('x')).contains(Symbol('x')) - items = [1,2,S.Infinity, 'ham', -1.1, Interval] - assert all(item in FiniteSet(items) for item in items) + items = [1, 2, S.Infinity, S('ham'), -1.1] + fset = FiniteSet(*items) + assert all(item in fset for item in items) + assert all(fset.contains(item) is True for item in items) assert Union(Interval(0, 1), Interval(2, 5)).contains(3) == True assert Union(Interval(0, 1), Interval(2, 5)).contains(6) == False @@ -355,6 +380,7 @@ def test_product_basic(): square = unit_line * unit_line assert (0,0) in square + assert 0 not in square assert (H, T) in coin ** 2 assert (.5,.5,.5) in square * unit_line assert (H, 3, 3) in coin * d6* d6 @@ -372,7 +398,7 @@ def test_product_basic(): assert (Interval(-10,10)**3).subset(Interval(-5,5)**3) assert not (Interval(-5,5)**3).subset(Interval(-10,10)**3) - raises(ValueError, "(Interval(-10,10)**2).subset(Interval(-5,5)**3)") + assert not (Interval(-10,10)**2).subset(Interval(-5,5)**3) assert square.subset(Interval(.2,.5)*FiniteSet(.5)) # segment in square @@ -408,3 +434,9 @@ def test_supinf(): assert FiniteSet(5,1,x,y,S.Infinity, S.NegativeInfinity).sup == S.Infinity assert FiniteSet(5,1,x,y,S.Infinity, S.NegativeInfinity).inf == S.NegativeInfinity assert FiniteSet('Ham', 'Eggs').sup == Max('Ham', 'Eggs') + +def test_universalset(): + U = S.UniversalSet + x = Symbol('x') + assert U.as_relational(x) == True + assert U.union(Interval(2,4)) == U diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index a642fa1468a3..0460b65dedfd 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -1,18 +1,19 @@ from sympy import (Symbol, Wild, sin, cos, exp, sqrt, pi, Function, Derivative, abc, Integer, Eq, symbols, Add, I, Float, log, Rational, Lambda, atan2, - cse, cot, tan, S, Tuple) -from sympy.utilities.pytest import XFAIL + cse, cot, tan, S, Tuple, Basic, Dict, Piecewise, oo) +from sympy.core.basic import _aresame +from sympy.utilities.pytest import XFAIL, raises def test_subs(): n3 = Rational(3) - x = Symbol("x") + x = Symbol('x') e = x - e = e.subs(x,n3) + e = e.subs(x, n3) assert e == Rational(3) e = 2*x assert e == 2*x - e = e.subs(x,n3) + e = e.subs(x, n3) assert e == Rational(6) def test_trigonometric(): @@ -20,74 +21,79 @@ def test_trigonometric(): n3 = Rational(3) e = (sin(x)**2).diff(x) assert e == 2*sin(x)*cos(x) - e = e.subs(x,n3) + e = e.subs(x, n3) assert e == 2*cos(n3)*sin(n3) e = (sin(x)**2).diff(x) assert e == 2*sin(x)*cos(x) - e = e.subs(sin(x),cos(x)) + e = e.subs(sin(x), cos(x)) assert e == 2*cos(x)**2 assert exp(pi).subs(exp, sin) == 0 assert cos(exp(pi)).subs(exp, sin) == 1 i = Symbol('i', integer=True) - assert tan(x).subs(x, pi/2) is S.NaN - assert cot(x).subs(x, pi) is S.NaN - assert cot(i*x).subs(x, pi) is S.NaN + zoo = S.ComplexInfinity + assert tan(x).subs(x, pi/2) is zoo + assert cot(x).subs(x, pi) is zoo + assert cot(i*x).subs(x, pi) is zoo assert tan(i*x).subs(x, pi/2) == tan(i*pi/2) - assert tan(i*x).subs(x, pi/2).subs(i, 1) is S.NaN + assert tan(i*x).subs(x, pi/2).subs(i, 1) is zoo o = Symbol('o', odd=True) - assert tan(o*x).subs(x, pi/2) is S.NaN + assert tan(o*x).subs(x, pi/2) == tan(o*pi/2) def test_powers(): x, y = symbols('x,y') assert sqrt(1 - sqrt(x)).subs(x, 4) == I - assert (sqrt(1-x**2)**3).subs(x, 2) == - 3 * I * sqrt(3) - assert (x ** Rational(1,3)).subs(x, 27) == 3 - assert (x ** Rational(1,3)).subs(x, -27) == 3 * (-1) ** Rational(1,3) - assert ((-x) ** Rational(1,3)).subs(x, 27) == 3 * (-1) ** Rational(1,3) + assert (sqrt(1 - x**2)**3).subs(x, 2) == - 3*I*sqrt(3) + assert (x**Rational(1, 3)).subs(x, 27) == 3 + assert (x**Rational(1, 3)).subs(x, -27) == 3*(-1)**Rational(1, 3) + assert ((-x)**Rational(1, 3)).subs(x, 27) == 3*(-1)**Rational(1, 3) n = Symbol('n', negative=True) assert (x**n).subs(x, 0) is S.Infinity assert exp(-1).subs(S.Exp1, 0) is S.Infinity assert (x**(4.0*y)).subs(x**(2.0*y), n) == n**2.0 def test_logexppow(): # no eval() - x = Symbol("x", real=True) - w = Symbol("w") - e = (3**(1+x)+2**(1+x))/(3**x+2**x) + x = Symbol('x', real=True) + w = Symbol('w') + e = (3**(1 + x) + 2**(1 + x))/(3**x + 2**x) assert e.subs(2**x, w) != e assert e.subs(exp(x*log(Rational(2))), w) != e def test_bug(): - x1 = Symbol("x1") - x2 = Symbol("x2") + x1 = Symbol('x1') + x2 = Symbol('x2') y = x1*x2 y.subs(x1, Float(3.0)) def test_subbug1(): - x = Symbol("x") - e = (x**x).subs(x,1) - e = (x**x).subs(x,1.0) + x = Symbol('x') + e = (x**x).subs(x, 1) + e = (x**x).subs(x, 1.0) def test_subbug2(): # Ensure this does not cause infinite recursion x = Symbol('x') assert Float(7.7).epsilon_eq(abs(x).subs(x, -7.7)) -def test_dict(): +def test_dict_set(): x = Symbol('x') - a,b,c = map(Wild, 'abc') + a, b, c = map(Wild, 'abc') f = 3*cos(4*x) r = f.match(a*cos(b*x)) assert r == {a: 3, b: 4} - e = a/b * sin(b*x) - assert e._subs_dict(r) == r[a]/r[b] * sin(r[b]*x) - assert e._subs_dict(r) == 3 * sin(4*x) / 4 + e = a/b*sin(b*x) + assert e.subs(r) == r[a]/r[b]*sin(r[b]*x) + assert e.subs(r) == 3*sin(4*x) / 4 + s = set(r.items()) + assert e.subs(s) == r[a]/r[b]*sin(r[b]*x) + assert e.subs(s) == 3*sin(4*x) / 4 - assert e.subs(r) == r[a]/r[b] * sin(r[b]*x) - assert e.subs(r) == 3 * sin(4*x) / 4 + assert e.subs(r) == r[a]/r[b]*sin(r[b]*x) + assert e.subs(r) == 3*sin(4*x) / 4 + assert x.subs(Dict((x, 1))) == 1 def test_dict_ambigous(): # see #467 x = Symbol('x') @@ -100,30 +106,40 @@ def test_dict_ambigous(): # see #467 df= {x:y, exp(x): y} dg= {z:y, exp(z): y} - assert f._subs_dict(df) == y**2 - assert g._subs_dict(dg) == y**2 + assert f.subs(df) == y**2 + assert g.subs(dg) == y**2 # and this is how order can affect the result - assert f .subs(x,y) .subs(exp(x),y) == y*exp(y) - assert f .subs(exp(x),y) .subs(x,y) == y**2 + assert f .subs(x, y) .subs(exp(x), y) == y*exp(y) + assert f .subs(exp(x), y) .subs(x, y) == y**2 + + # length of args and count_ops are the same so + # default_sort_key resolves ordering...if one + # doesn't want this result then an unordered + # sequence should not be used. + e = 1 + x*y + assert e.subs({x: y, y: 2}) == 5 + # here, there are no obviously clashing keys or values + # but the results depend on the order + assert exp(x/2 + y).subs(dict([(exp(y + 1), 2),(x, 2)])) == exp(y + 1) def test_deriv_sub_bug3(): - x = Symbol("x") - y = Symbol("y") - f = Function("f") + x = Symbol('x') + y = Symbol('y') + f = Function('f') pat = Derivative(f(x), x, x) assert pat.subs(y, y**2) == Derivative(f(x), x, x) assert pat.subs(y, y**2) != Derivative(f(x), x) def test_equality_subs1(): - f = Function("f") + f = Function('f') x = abc.x eq = Eq(f(x)**2, x) res = Eq(Integer(16), x) assert eq.subs(f(x), 4) == res def test_equality_subs2(): - f = Function("f") + f = Function('f') x = abc.x eq = Eq(f(x)**2, 16) assert bool(eq.subs(f(x), 3)) == False @@ -141,10 +157,10 @@ def test_subs_dict1(): assert (1 + x*y).subs(x, pi) == 1 + pi*y assert (1 + x*y).subs({x:pi, y:2}) == 1 + 2*pi - c2,c3,q1p,q2p,c1,s1,s2,s3 = symbols('c2 c3 q1p q2p c1 s1 s2 s3') + c2, c3, q1p, q2p, c1, s1, s2, s3 = symbols('c2 c3 q1p q2p c1 s1 s2 s3') test = (c2**2*q2p*c3 + c1**2*s2**2*q2p*c3 + s1**2*s2**2*q2p*c3 - c1**2*q1p*c2*s3 - s1**2*q1p*c2*s3) - assert (test.subs({c1**2 : 1-s1**2, c2**2 : 1-s2**2, c3**3: 1-s3**2}) + assert (test.subs({c1**2 : 1 - s1**2, c2**2 : 1 - s2**2, c3**3: 1 - s3**2}) == c3*q2p*(1 - s2**2) + c3*q2p*s2**2*(1 - s1**2) - c2*q1p*s3*(1 - s1**2) + c3*q2p*s1**2*s2**2 - c2*q1p*s3*s1**2) @@ -170,7 +186,8 @@ def test_mul(): assert (x**3*A).subs(x**2*A, a) == a*x assert (x**2*A*B).subs(x**2*B, a) == a*A assert (x**2*A*B).subs(x**2*A, a) == a*B - assert (b*A**3/(a**3*c**3)).subs(a**4*c**3*A**3/b**4, z) == b*A**3/(a**3*c**3) + assert (b*A**3/(a**3*c**3)).subs(a**4*c**3*A**3/b**4, z) == \ + b*A**3/(a**3*c**3) assert (6*x).subs(2*x, y) == 3*y assert (y*exp(3*x/2)).subs(y*exp(x), 2) == 2*exp(x/2) assert (y*exp(3*x/2)).subs(y*exp(x), 2) == 2*exp(x/2) @@ -189,47 +206,47 @@ def test_subs_simple(): a = symbols('a', commutative=True) x = symbols('x', commutative=False) - assert (2*a ).subs(1,3) == 2*a - assert (2*a ).subs(2,3) == 3*a - assert (2*a ).subs(a,3) == 6 - assert sin(2).subs(1,3) == sin(2) - assert sin(2).subs(2,3) == sin(3) - assert sin(a).subs(a,3) == sin(3) + assert (2*a).subs(1, 3) == 2*a + assert (2*a).subs(2, 3) == 3*a + assert (2*a).subs(a, 3) == 6 + assert sin(2).subs(1, 3) == sin(2) + assert sin(2).subs(2, 3) == sin(3) + assert sin(a).subs(a, 3) == sin(3) - assert (2*x ).subs(1,3) == 2*x - assert (2*x ).subs(2,3) == 3*x - assert (2*x ).subs(x,3) == 6 - assert sin(x).subs(x,3) == sin(3) + assert (2*x).subs(1, 3) == 2*x + assert (2*x).subs(2, 3) == 3*x + assert (2*x).subs(x, 3) == 6 + assert sin(x).subs(x, 3) == sin(3) def test_subs_constants(): - a,b = symbols('a b', commutative=True) - x,y = symbols('x y', commutative=False) + a, b = symbols('a b', commutative=True) + x, y = symbols('x y', commutative=False) - assert (a*b ).subs(2*a,1) == a*b - assert (1.5*a*b).subs(a,1) == 1.5*b - assert (2*a*b).subs(2*a,1) == b - assert (2*a*b).subs(4*a,1) == 2*a*b + assert (a*b).subs(2*a, 1) == a*b + assert (1.5*a*b).subs(a, 1) == 1.5*b + assert (2*a*b).subs(2*a, 1) == b + assert (2*a*b).subs(4*a, 1) == 2*a*b - assert (x*y ).subs(2*x,1) == x*y - assert (1.5*x*y).subs(x,1) == 1.5*y - assert (2*x*y).subs(2*x,1) == y - assert (2*x*y).subs(4*x,1) == 2*x*y + assert (x*y).subs(2*x, 1) == x*y + assert (1.5*x*y).subs(x, 1) == 1.5*y + assert (2*x*y).subs(2*x, 1) == y + assert (2*x*y).subs(4*x, 1) == 2*x*y def test_subs_commutative(): - a,b,c,d,K = symbols('a b c d K', commutative=True) - - assert (a*b ).subs(a*b,K) == K - assert (a*b*a*b).subs(a*b,K) == K**2 - assert (a*a*b*b).subs(a*b,K) == K**2 - assert (a*b*c*d).subs(a*b*c,K) == d*K - assert (a*b**c ).subs(a,K) == K*b**c - assert (a*b**c ).subs(b,K) == a*K**c - assert (a*b**c ).subs(c,K) == a*b**K - assert (a*b*c*b*a ).subs(a*b,K) == c*K**2 - assert (a**3*b**2*a).subs(a*b,K) == a**2*K**2 + a, b, c, d, K = symbols('a b c d K', commutative=True) + + assert (a*b).subs(a*b, K) == K + assert (a*b*a*b).subs(a*b, K) == K**2 + assert (a*a*b*b).subs(a*b, K) == K**2 + assert (a*b*c*d).subs(a*b*c, K) == d*K + assert (a*b**c).subs(a, K) == K*b**c + assert (a*b**c).subs(b, K) == a*K**c + assert (a*b**c).subs(c, K) == a*b**K + assert (a*b*c*b*a).subs(a*b, K) == c*K**2 + assert (a**3*b**2*a).subs(a*b, K) == a**2*K**2 def test_subs_noncommutative(): - w,x,y,z,L = symbols('w x y z L', commutative=False) + w, x, y, z, L = symbols('w x y z L', commutative=False) assert (x*y).subs(x*y, L) == L assert (w*y*x).subs(x*y, L) == w*y*x @@ -245,62 +262,57 @@ def test_subs_noncommutative(): assert (w*x*y*y*w*x*x*y*x*y*y*x*y).subs(x*y, L) == w*L*y*w*x*L**2*y*L def test_subs_basic_funcs(): - a,b,c,d,K = symbols('a b c d K', commutative=True) - w,x,y,z,L = symbols('w x y z L', commutative=False) - - assert (x+y ).subs(x+y,L) == L - assert (x-y ).subs(x-y,L) == L - assert (x/y ).subs(x,L) == L/y - assert (x**y ).subs(x,L) == L**y - assert (x**y ).subs(y,L) == x**L - assert ( (a-c)/b ).subs(b,K) == (a-c)/K - assert (exp(x*y-z)).subs(x*y,L) == exp(L-z) - assert (a*exp(x*y-w*z)+b*exp(x*y+w*z)).subs(z,0) == a*exp(x*y)+b*exp(x*y) - assert ((a-b)/(c*d-a*b)).subs(c*d-a*b,K) == (a-b)/K - assert (w*exp(a*b-c)*x*y/4).subs(x*y,L) == w*exp(a*b-c)*L/4 - -@XFAIL -def test_subs_basic_funcs_division_bug(): - # Fails because of division - a,b,c,K = symbols('a b c K', commutative=True) - assert (a/(b*c)).subs(b*c, K) == a/K + a, b, c, d, K = symbols('a b c d K', commutative=True) + w, x, y, z, L = symbols('w x y z L', commutative=False) + + assert (x + y).subs(x + y, L) == L + assert (x - y).subs(x - y, L) == L + assert (x/y).subs(x, L) == L/y + assert (x**y).subs(x, L) == L**y + assert (x**y).subs(y, L) == x**L + assert ((a - c)/b).subs(b, K) == (a - c)/K + assert (exp(x*y - z)).subs(x*y, L) == exp(L - z) + assert (a*exp(x*y - w*z) + b*exp(x*y + w*z)).subs(z, 0) == a*exp(x*y) + b*exp(x*y) + assert ((a - b)/(c*d - a*b)).subs(c*d - a*b, K) == (a - b)/K + assert (w*exp(a*b - c)*x*y/4).subs(x*y, L) == w*exp(a*b - c)*L/4 def test_subs_wild(): - R, S, T, U = symbols('R, S, T, U', cls=Wild) + R, S, T, U = symbols('R,S,T,U', cls=Wild) - assert (R*S ).subs(R*S,T) == T - assert (S*R ).subs(R*S,T) == T - assert (R+S ).subs(R+S,T) == T - assert (R**S).subs(R,T) == T**S - assert (R**S).subs(S,T) == R**T - assert (R*S**T).subs(R,U) == U*S**T - assert (R*S**T).subs(S,U) == R*U**T - assert (R*S**T).subs(T,U) == R*S**U + assert (R*S).subs(R*S, T) == T + assert (S*R).subs(R*S, T) == T + assert (R + S).subs(R + S, T) == T + assert (R**S).subs(R, T) == T**S + assert (R**S).subs(S, T) == R**T + assert (R*S**T).subs(R, U) == U*S**T + assert (R*S**T).subs(S, U) == R*U**T + assert (R*S**T).subs(T, U) == R*S**U def test_subs_mixed(): - a,b,c,d,K = symbols('a b c d K', commutative=True) - w,x,y,z,L = symbols('w x y z L', commutative=False) - R, S, T, U = symbols('R, S, T, U', cls=Wild) + a, b, c, d, K = symbols('a b c d K', commutative=True) + w, x, y, z, L = symbols('w x y z L', commutative=False) + R, S, T, U = symbols('R,S,T,U', cls=Wild) - assert (a*x*y).subs(x*y,L) == a*L - assert (a*b*x*y*x).subs(x*y,L) == a*b*L*x - assert (R*x*y*exp(x*y)).subs(x*y,L) == R*L*exp(L) - assert (a*x*y*y*x - x*y*z*exp(a*b)).subs(x*y,L) == a*L*y*x - L*z*exp(a*b) - assert (c*y*x*y*x**(R*S - a*b) - T*(a*R*b*S)).subs(x*y,L).subs(a*b,K).subs(R*S,U) == c*y*L*x**(U-K)-T*(U*K) + assert (a*x*y).subs(x*y, L) == a*L + assert (a*b*x*y*x).subs(x*y, L) == a*b*L*x + assert (R*x*y*exp(x*y)).subs(x*y, L) == R*L*exp(L) + assert (a*x*y*y*x - x*y*z*exp(a*b)).subs(x*y, L) == a*L*y*x - L*z*exp(a*b) + e = c*y*x*y*x**(R*S - a*b) - T*(a*R*b*S) + assert e.subs(x*y, L).subs(a*b, K).subs(R*S, U) == c*y*L*x**(U - K) - T*(U*K) def test_division(): - a,b,c = symbols('a b c', commutative=True) - x,y,z = symbols('x y z', commutative=True) + a, b, c = symbols('a b c', commutative=True) + x, y, z = symbols('x y z', commutative=True) assert (1/a).subs(a, c) == 1/c assert (1/a**2).subs(a, c) == 1/c**2 - assert (1/a**2).subs(a, -2) == Rational(1,4) - assert (-(1/a**2)).subs(a, -2) == -Rational(1,4) + assert (1/a**2).subs(a, -2) == Rational(1, 4) + assert (-(1/a**2)).subs(a, -2) == -Rational(1, 4) assert (1/x).subs(x, z) == 1/z assert (1/x**2).subs(x, z) == 1/z**2 - assert (1/x**2).subs(x, -2) == Rational(1,4) - assert (-(1/x**2)).subs(x, -2) == -Rational(1,4) + assert (1/x**2).subs(x, -2) == Rational(1, 4) + assert (-(1/x**2)).subs(x, -2) == -Rational(1, 4) #issue 2261 assert (1/x).subs(x, 0) == 1/S(0) @@ -313,9 +325,10 @@ def test_add(): assert (a**2 - b - c).subs(a**2 - c, d) in [d - b, a**2 - b - c] assert (a**2 - x - c).subs(a**2 - c, d) in [d - x, a**2 - x - c] assert (a**2 - b - sqrt(a)).subs(a**2 - sqrt(a), c) == c - b - assert (a+b+exp(a+b)).subs(a+b,c) == c + exp(c) - assert (c+b+exp(c+b)).subs(c+b,a) == a + exp(a) - + assert (a + b + exp(a + b)).subs(a + b, c) == c + exp(c) + assert (c + b + exp(c + b)).subs(c + b, a) == a + exp(a) + assert (a + b + c + d).subs(b + c, x) == a + d + x + assert (a + b + c + d).subs(-b - c, x) == a + d - x assert ((x + 1)*y).subs(x + 1, t) == t*y assert ((-x - 1)*y).subs(x + 1, t) == -t*y assert ((x - 1)*y).subs(x + 1, t) == y*(t - 2) @@ -326,29 +339,40 @@ def test_add(): assert e.subs(Add(*e.args[:2]), d) == d + e.args[2] assert e.subs(a**2 - c, d) == d - b + # the fallback should recognize when a change has + # been made; while .1 == Rational(1, 10) they are not the same + # and the change should be made + assert (0.1 + a).subs(0.1, Rational(1, 10)) == Rational(1, 10) + a + + e = (-x*(-y + 1) - y*(y - 1)) + ans = (-x*(x) - y*(-x)).expand() + assert e.subs(-y + 1, x) == ans + def test_subs_issue910(): - assert (I*Symbol("a")).subs(1, 2) == I*Symbol("a") + assert (I*Symbol('a')).subs(1, 2) == I*Symbol('a') def test_functions_subs(): x, y = map(Symbol, 'xy') f, g = map(Function, 'fg') l = Lambda((x, y), sin(x) + y) - assert (g(y, x)+cos(x)).subs(g, l) == sin(y) + x + cos(x) + assert (g(y, x) + cos(x)).subs(g, l) == sin(y) + x + cos(x) assert (f(x)**2).subs(f, sin) == sin(x)**2 - assert (f(x,y)).subs(f,log) == log(x,y) - assert (f(x,y)).subs(f,sin) == f(x,y) - assert (sin(x)+atan2(x,y)).subs([[atan2,f],[sin,g]]) == f(x,y) + g(x) - assert (g(f(x+y, x))).subs([[f, l], [g, exp]]) == exp(x + sin(x + y)) + assert (f(x, y)).subs(f, log) == log(x, y) + assert (f(x, y)).subs(f, sin) == f(x, y) + assert (sin(x) + atan2(x, y)).subs([[atan2, f], [sin, g]]) == f(x, y) + g(x) + assert (g(f(x + y, x))).subs([[f, l], [g, exp]]) == exp(x + sin(x + y)) def test_derivative_subs(): x = Symbol('x') y = Symbol('y') f = Function('f') assert Derivative(f(x), x).subs(f(x), y) != 0 - assert Derivative(f(x), x).subs(f(x), y).subs(y, f(x)) == Derivative(f(x), x) + assert Derivative(f(x), x).subs(f(x), y).subs(y, f(x)) == \ + Derivative(f(x), x) # issues 1986, 1938 assert cse(Derivative(f(x), x) + f(x))[1][0].has(Derivative) - assert cse(Derivative(f(x, y), x) + Derivative(f(x, y), y))[1][0].has(Derivative) + assert cse(Derivative(f(x, y), x) + + Derivative(f(x, y), y))[1][0].has(Derivative) def test_issue2185(): x = Symbol('x') @@ -365,8 +389,37 @@ def test_subs_iter(): assert x.subs(Tuple((x, y))) == y def test_subs_dict(): - x, y, z = symbols('x,y,z') - (2*x + y + z).subs(dict(x=1, y=2)) == 4 + z + a, b, c, d, e = symbols('a b c d e') + x, y, z = symbols('x y z') + + assert (2*x + y + z).subs(dict(x=1, y=2)) == 4 + z + + l = [(sin(x), 2), (x, 1)] + assert (sin(x)).subs(l) == \ + (sin(x)).subs(dict(l)) == 2 + assert sin(x).subs(reversed(l)) == sin(1) + + expr = sin(2*x) + sqrt(sin(2*x))*cos(2*x)*sin(exp(x)*x) + reps = dict([ + (sin(2*x), c), + (sqrt(sin(2*x)), a), + (cos(2*x), b), + (exp(x), e), + (x, d), + ]) + assert expr.subs(reps) == c + a*b*sin(d*e) + + l = [(x, 3), (y, x**2)] + assert (x + y).subs(l) == 3 + x**2 + assert (x + y).subs(reversed(l)) == 12 + + # If changes are made to convert lists into dictionaries and do + # a dictionary-lookup replacement, these tests will help to catch + # some logical errors that might occur + l = [(y, z + 2), (1 + z, 5), (z, 2)] + assert (y - 1 + 3*x).subs(l) == 5 + 3*x + l = [(y, z + 2), (z, 3)] + assert (y - 2).subs(l) == 3 def test_no_arith_subs_on_floats(): a, x, y = symbols('a,x,y') @@ -382,3 +435,59 @@ def test_no_arith_subs_on_floats(): (x + y + 3.0).subs(x + 3.0, a) == a + y (x + y + 3.0).subs(x + 2.0, a) == x + y + 3.0 + +@XFAIL +def test_issue_2261() : + x = Symbol('x') + assert (1/x).subs(x, 0) == 1/S(0) + +def test_issue_2552(): + a, b, c, K = symbols('a b c K', commutative=True) + x, y, z = symbols('x y z') + assert (a/(b*c)).subs(b*c, K) == a/K + assert (a/(b**2*c**3)).subs(b*c, K) == a/(c*K**2) + assert (1/(x*y)).subs(x*y, 2) == S.Half + assert ((1 + x*y)/(x*y)).subs(x*y, 1) == 2 + assert (x*y*z).subs(x*y,2) == 2*z + assert ((1 + x*y)/(x*y)/z).subs(x*y, 1) == 2/z + +def test_issue_2976(): + assert Tuple(1, True).subs(1, 2) == Tuple(2, True) + +def test_issue_2980(): + # since x + 2.0 == x + 2 we can't do a simple equality test + x = symbols('x') + assert _aresame((x + 2.0).subs(2, 3), x + 2.0) + assert _aresame((x + 2.0).subs(2.0, 3), x + 3) + assert not _aresame(x + 2, x + 2.0) + assert not _aresame(Basic(cos, 1), Basic(cos, 1.)) + assert _aresame(cos, cos) + assert not _aresame(1, S(1)) + assert not _aresame(x, symbols('x', positive=True)) + +def test_issue_1581(): + N = Symbol('N') + assert N.subs(dict(N=3)) == 3 + +def test_issue_3059(): + from sympy.abc import x, y + assert (x - 1).subs(1, y) == x - y + assert (x - 1).subs(-1, y) == x + y + assert (x - oo).subs(oo, y) == x - y + assert (x - oo).subs(-oo, y) == x + y + +def test_Function_subs(): + from sympy.abc import x, y + f, g, h, i = symbols('f,g,h,i', cls=Function) + p = Piecewise((g, x < -1), (g(x), x <= 1)) + assert p.subs(g, h) == Piecewise((h, x < -1), (h(x), x <= 1)) + assert (f(y) + g(x)).subs({f:h,g:i}) == i(x) + h(y) + +def test_simultaneous_subs(): + from sympy.abc import x, y + reps = {x: 0, y: 0} + assert (x/y).subs(reps) != (y/x).subs(reps) + assert (x/y).subs(reps, simultaneous=True) == (y/x).subs(reps, simultaneous=True) + reps = reps.items() + assert (x/y).subs(reps) != (y/x).subs(reps) + assert (x/y).subs(reps, simultaneous=True) == (y/x).subs(reps, simultaneous=True) diff --git a/sympy/functions/__init__.py b/sympy/functions/__init__.py index b4710215c574..4cc9eead837e 100644 --- a/sympy/functions/__init__.py +++ b/sympy/functions/__init__.py @@ -19,7 +19,7 @@ from combinatorial.factorials import binomial, factorial2 from combinatorial.numbers import fibonacci, lucas, harmonic, bernoulli, bell, euler, catalan -from elementary.miscellaneous import sqrt, root, Min, Max, Id, real_root, round +from elementary.miscellaneous import sqrt, root, Min, Max, Id, real_root from elementary.complexes import (re, im, sign, Abs, conjugate, arg, polar_lift, periodic_argument, unbranched_argument, principal_branch) diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py index 3a2c1e4b2777..9d8cadb15e38 100644 --- a/sympy/functions/elementary/exponential.py +++ b/sympy/functions/elementary/exponential.py @@ -45,8 +45,14 @@ def as_numer_denom(self): >>> exp(x).as_numer_denom() (exp(x), 1) """ - if self.args[0].as_coeff_mul()[0].is_negative: - return S.One, exp(-self.args[0]) + # this should be the same as Pow.as_numer_denom wrt + # exponent handling + exp = self.exp + neg_exp = exp.is_negative + if not neg_exp and not exp.is_real: + neg_exp = _coeff_isneg(exp) + if neg_exp: + return S.One, self.func(-exp) return self, S.One @property @@ -87,8 +93,27 @@ def _eval_is_zero(self): return (self.args[0] is S.NegativeInfinity) def _eval_power(b, e): - """exp(b[0])**e -> exp(b[0]*e)""" - return b.func(b.args[0] * e) + """exp(arg)**e -> exp(arg*e) if assumptions allow it. + """ + f = b.func + be = b.exp + rv = f(be*e) + if e.is_integer: + return rv + if be.is_real: + return rv + # "is True" needed below; exp.is_polar returns + if f.is_polar is True: + return rv + if e.is_polar: + return rv + if be.is_polar: + return rv + besmall = abs(be) <= S.Pi + if besmall: + return rv + elif besmall is False and e.is_Rational and e.q == 2: + return -rv def _eval_expand_power_exp(self, deep=True, **hints): if deep: @@ -303,16 +328,14 @@ def as_real_imag(self, deep=True, **hints): return (exp(re)*cos, exp(re)*sin) def _eval_subs(self, old, new): - if self == old: - return new arg = self.args[0] o = old if old.is_Pow: # handle (exp(3*log(x))).subs(x**2, z) -> z**(3/2) - old = exp(old.exp*log(old.base)) - if old.func is exp: + o = exp(o.exp*log(o.base)) + if o.func is exp: # exp(a*expr) .subs( exp(b*expr), y ) -> y ** (a/b) a, expr_terms = self.args[0].as_coeff_mul() - b, expr_terms_= old.args[0].as_coeff_mul() + b, expr_terms_= o.args[0].as_coeff_mul() if expr_terms == expr_terms_: return new**(a/b) @@ -320,28 +343,27 @@ def _eval_subs(self, old, new): if arg.is_Add: # exp(2*x+a).subs(exp(3*x),y) -> y**(2/3) * exp(a) # exp(exp(x) + exp(x**2)).subs(exp(exp(x)), w) -> w * exp(exp(x**2)) - oarg = old.args[0] + oarg = o.args[0] new_l = [] - old_al = [] + o_al = [] coeff2, terms2 = oarg.as_coeff_mul() for a in arg.args: - a = a._eval_subs(old, new) + a = a._subs(o, new) coeff1, terms1 = a.as_coeff_mul() if terms1 == terms2: new_l.append(new**(coeff1/coeff2)) else: - old_al.append(a._eval_subs(old, new)) + o_al.append(a._subs(o, new)) if new_l: - new_l.append(self.func(Add(*old_al))) + new_l.append(self.func(Add(*o_al))) r = Mul(*new_l) return r - if old is S.Exp1: + if o is S.Exp1: # treat this however Pow is being treated u = C.Dummy('u') return (u**self.args[0]).subs(u, new) - old = o - return Function._eval_subs(self, old, new) + return Function._eval_subs(self, o, new) def _eval_is_real(self): return self.args[0].is_real @@ -708,3 +730,4 @@ def fdiff(self, argindex=1): else: raise ArgumentIndexError(self, argindex) +from sympy.core.function import _coeff_isneg diff --git a/sympy/functions/elementary/miscellaneous.py b/sympy/functions/elementary/miscellaneous.py index 71b0da7dc64e..044900aef467 100644 --- a/sympy/functions/elementary/miscellaneous.py +++ b/sympy/functions/elementary/miscellaneous.py @@ -503,73 +503,3 @@ def _rel_inversed(cls, x, y): Check if x > y. """ return (x > y) - -_pyround = round -def round(x, p=0): - """Return x rounded to the given decimal place. If x is not an Expr, - Python's round function is employed. - - Examples - ======== - >>> from sympy import round, pi, S, Number - >>> round(S(10.5)) - 11. - >>> round(pi) - 3. - >>> round(pi, 2) - 3.14 - - If x is not a SymPy Expr then Python's round is used and it returns - a Python type, not a SymPy Number: - - >>> isinstance(round(543210, -2), Number) - False - >>> round(S(543210), -2) - 5.432e+5 - >>> _.is_Number - True - - """ - from sympy.functions.elementary.exponential import log - from sympy.mpmath.libmp import prec_to_dps - - if not isinstance(x, Expr): - return _pyround(x, p) - if not x.is_number: - raise TypeError('%s is not a number' % x) - if not x.is_real: - raise TypeError("can't convert complex to int") - if not x: - return x - p = int(p) - - precs = [f._prec for f in x.atoms(C.Float)] - dps = prec_to_dps(max(precs)) if precs else None - - xpos = abs(x.n()) - try: - mag_first_dig = int(ceil(log10(xpos))) - except (ValueError, OverflowError): - mag_first_dig = int(ceil(C.Float(mpf_log(xpos._mpf_, 53))/log(10))) - # check that we aren't off by 1 - if (xpos/10**mag_first_dig) >= 1: - mag_first_dig += 1 - assert .1 <= (xpos/10**mag_first_dig) < 1 - allow = digits_needed = mag_first_dig + p - if dps is not None and allow > dps: - allow = dps - mag = Pow(10, p) # magnitude needed to bring digit p to units place - x += 1/(2*mag) # add the half for rounding - i10 = 10*mag*x.n((dps if dps is not None else digits_needed) + 1) - rv = Integer(i10)//10 - q = 1 - if p > 0: - q = mag - elif p < 0: - rv /= mag - rv = Rational(rv, q) - if rv.is_Integer: - # use str or else it won't be a float - return C.Float(str(rv), digits_needed) - else: - return C.Float(rv, allow) diff --git a/sympy/functions/elementary/piecewise.py b/sympy/functions/elementary/piecewise.py index 198c7e32b1c8..55fc721ee963 100644 --- a/sympy/functions/elementary/piecewise.py +++ b/sympy/functions/elementary/piecewise.py @@ -1,4 +1,4 @@ -from sympy.core import Add, Basic, Expr, Function, S, sympify, Tuple +from sympy.core import Add, Basic, Expr, Function, S, Set, sympify, Tuple from sympy.core.relational import Equality, Relational from sympy.logic.boolalg import Boolean @@ -324,6 +324,33 @@ def _eval_interval(self, sym, a, b): ret_fun += expr._eval_interval(sym, max(a, int_a), min(b, int_b)) return mul * ret_fun + def _eval_subs(self, old, new): + """ + Piecewise conditions may contain Sets whose modifications + requires the use of contains rather than substitution. They + may also contain bool which are not of Basic type. + """ + ecs = list(self.exprcondpairs) + for i, (e, c) in enumerate(ecs): + try: + e = e._subs(old, new) + except TypeError: + if e != old: + continue + e = new + + if isinstance(c, Set): + # What do we do if there are more than one symbolic + # variable. Which do we put pass to Set.contains? + c = c.contains(new) + elif isinstance(c, Basic): + c = c._subs(old, new) + + ecs[i] = e, c + ecs.append(self.otherwise) + + return Piecewise(*ecs) + def _eval_nseries(self, x, n, logx): new_args = map(lambda ec: (ec[0]._eval_nseries(x, n, logx), ec[1]), self.exprcondpairs) new_args.append( self.otherwise._eval_nseries(x, n, logx) ) diff --git a/sympy/functions/elementary/tests/test_complexes.py b/sympy/functions/elementary/tests/test_complexes.py index 152ea6db1e41..8530761a5580 100644 --- a/sympy/functions/elementary/tests/test_complexes.py +++ b/sympy/functions/elementary/tests/test_complexes.py @@ -2,6 +2,11 @@ pi, arg, conjugate, expand, exp, sin, cos, Function, Abs, zoo from sympy.utilities.pytest import XFAIL +from sympy.utilities.randtest import comp +def N_equals(a, b): + """Check whether two complex numbers are numerically close""" + return comp(a.n(), b.n(), 1.e-6) + def test_re(): x, y = symbols('x,y') @@ -112,7 +117,7 @@ def test_sign(): def test_sign_issue_3068(): n = pi**1000 i = int(n) - assert round(n - i) == 1 # doesn't hang + assert (n - i).round() == 1 # doesn't hang assert sign(n - i) == 1 # perhaps it's not possible to get the sign right when # only 1 digit is being requested for this situtation; @@ -128,6 +133,8 @@ def test_Abs(): assert Abs(1) == 1 assert Abs(-1)== 1 assert Abs(nan) == nan + assert Abs(I * pi) == pi + x = Symbol('x',real=True) n = Symbol('n',integer=True) assert x**(2*n) == Abs(x)**(2*n) @@ -235,17 +242,12 @@ def test_periodic_argument(): x = Symbol('x') p = Symbol('p', positive = True) - def tn(a, b): - from sympy.utilities.randtest import test_numerically - from sympy import Dummy - return test_numerically(a, b, Dummy('x')) - assert unbranched_argument(2 + I) == periodic_argument(2 + I, oo) assert unbranched_argument(1 + x) == periodic_argument(1 + x, oo) - assert tn(unbranched_argument((1+I)**2), pi/2) - assert tn(unbranched_argument((1-I)**2), -pi/2) - assert tn(periodic_argument((1+I)**2, 3*pi), pi/2) - assert tn(periodic_argument((1-I)**2, 3*pi), -pi/2) + assert N_equals(unbranched_argument((1+I)**2), pi/2) + assert N_equals(unbranched_argument((1-I)**2), -pi/2) + assert N_equals(periodic_argument((1+I)**2, 3*pi), pi/2) + assert N_equals(periodic_argument((1-I)**2, 3*pi), -pi/2) assert unbranched_argument(principal_branch(x, pi)) \ == periodic_argument(x, pi) @@ -265,7 +267,7 @@ def tn(a, b): @XFAIL def test_principal_branch_fail(): # TODO XXX why does abs(x)._eval_evalf() not fall back to global evalf? - assert tn(principal_branch((1 + I)**2, pi/2), 0) + assert N_equals(principal_branch((1 + I)**2, pi/2), 0) def test_principal_branch(): from sympy import principal_branch, polar_lift, exp_polar @@ -283,20 +285,12 @@ def test_principal_branch(): principal_branch(exp_polar(I*pi)*x, 2*pi) assert principal_branch(neg*exp_polar(pi*I), 2*pi) == neg*exp_polar(-I*pi) - def tn(a, b): - from sympy.utilities.randtest import test_numerically - from sympy import Dummy - return test_numerically(a, b, Dummy('x')) - assert tn(principal_branch((1 + I)**2, 2*pi), 2*I) - assert tn(principal_branch((1 + I)**2, 3*pi), 2*I) - assert tn(principal_branch((1 + I)**2, 1*pi), 2*I) + assert N_equals(principal_branch((1 + I)**2, 2*pi), 2*I) + assert N_equals(principal_branch((1 + I)**2, 3*pi), 2*I) + assert N_equals(principal_branch((1 + I)**2, 1*pi), 2*I) # test argument sanitization assert principal_branch(x, I).func is principal_branch assert principal_branch(x, -4).func is principal_branch assert principal_branch(x, -oo).func is principal_branch assert principal_branch(x, zoo).func is principal_branch - -@XFAIL -def test_issue_2453(): - assert abs(I * pi) == pi diff --git a/sympy/functions/elementary/tests/test_exponential.py b/sympy/functions/elementary/tests/test_exponential.py index 63ac25934379..5a410338e4a4 100644 --- a/sympy/functions/elementary/tests/test_exponential.py +++ b/sympy/functions/elementary/tests/test_exponential.py @@ -85,9 +85,10 @@ def test_exp_infinity(): assert exp(y*I*oo) != nan def test_exp_subs(): - x,y = symbols('x,y') - assert (exp(3*log(x), evaluate=False)).subs(x**3,y**3) == x**3 - assert (exp(3*log(x), evaluate=False)).subs(x**2,5) == x**3 + x, y = symbols('x,y') + e = (exp(3*log(x), evaluate=False)) + assert e.subs(x**3, y**3) == e + assert e.subs(x**2, 5) == e assert exp(3*log(x)).subs(x**2, y) == x**3 assert exp(5*x).subs(exp(7*x),y) == y**Rational(5,7) assert exp(2*x + 7).subs(exp(3*x),y) == y**Rational(2,3) * exp(7) @@ -289,8 +290,12 @@ def test_as_numer_denom(): assert exp(-x).as_numer_denom() == (1, exp(x)) assert exp(-2*x).as_numer_denom() == (1, exp(2*x)) assert exp(-2).as_numer_denom() == (1, exp(2)) - assert exp(n).as_numer_denom() == (exp(n), 1) - assert exp(-n).as_numer_denom() == (1, exp(n)) + assert exp(n).as_numer_denom() == (1, exp(-n)) + assert exp(-n).as_numer_denom() == (exp(-n), 1) + assert exp(-I*x).as_numer_denom() == (1, exp(I*x)) + assert exp(-I*n).as_numer_denom() == (1, exp(I*n)) + assert exp(-n).as_numer_denom() == (exp(-n), 1) + def test_polar(): x, y = symbols('x y', polar=True) diff --git a/sympy/functions/elementary/tests/test_miscellaneous.py b/sympy/functions/elementary/tests/test_miscellaneous.py index 543bf3ef9419..d877f433dca7 100644 --- a/sympy/functions/elementary/tests/test_miscellaneous.py +++ b/sympy/functions/elementary/tests/test_miscellaneous.py @@ -1,8 +1,7 @@ from sympy.core.symbol import Symbol from sympy.core.numbers import Rational from sympy.utilities.pytest import raises -_pyround = round -from sympy.functions.elementary.miscellaneous import sqrt, root, Min, Max, real_root, round +from sympy.functions.elementary.miscellaneous import sqrt, root, Min, Max, real_root from sympy import S, Float, I, cos, sin, oo, pi, Add def test_Min(): @@ -164,64 +163,3 @@ def test_nthroot(): r2 = r1**2 r3 = root(-1, 4) assert real_root(r1 + r2 + r3) == -1 + r2 + r3 - -def test_round(): - from sympy.abc import x - - assert round(Float('0.1249999'), 2) == 0.12 - d20 = 12345678901234567890 - ans = round(S(d20), 2) - assert ans.is_Float and ans == d20 - ans = round(S(d20), -2) - assert ans.is_Float and ans == 12345678901234567900 - assert round(S('1/7'), 4) == 0.1429 - assert round(S('.[12345]'), 4) == 0.1235 - assert round(S('.1349'), 2) == 0.13 - n = S(12345) - ans = round(n) - assert ans.is_Float - assert ans == n - ans = round(n, 1) - assert ans.is_Float - assert ans == n - ans = round(n, 4) - assert ans.is_Float - assert ans == n - assert round(n, -1) == 12350 - - r = round(n, -4) - assert r == 10000 - # in fact, it should equal many values since __eq__ - # compares at equal precision - assert all(r == i for i in range(9984, 10049)) - - assert round(n, -5) == 0 - - assert round(pi + sqrt(2), 2) == 4.56 - assert round(10*(pi + sqrt(2)), -1) == 50 - raises(TypeError, 'round(x + 2, 2)') - assert round(S(2.3), 1) == 2.3 - e = round(12.345, 2) - assert e == _pyround(12.345, 2) - assert type(e) is float - - assert round(Float(.3, 3) + 2*pi) == 7 - assert round(Float(.3, 3) + 2*pi*100) == 629 - assert round(Float(.03, 3) + 2*pi/100, 5) == 0.09283 - assert round(Float(.03, 3) + 2*pi/100, 4) == 0.0928 - - assert round(S.Zero) == 0 - - a = (Add(1, Float('1.'+'9'*27, ''), evaluate=0)) - assert round(a, 10) == Float('3.0000000000','') - assert round(a, 25) == Float('3.0000000000000000000000000','') - assert round(a, 26) == Float('3.00000000000000000000000000','') - assert round(a, 27) == Float('2.999999999999999999999999999','') - assert round(a, 30) == Float('2.999999999999999999999999999','') - - raises(TypeError, 'round(x)') - raises(TypeError, 'round(1 + 3*I)') - - # exact magnitude of 10 - assert str(round(S(1))) == '1.' - assert str(round(S(100))) == '100.' diff --git a/sympy/functions/elementary/tests/test_trigonometric.py b/sympy/functions/elementary/tests/test_trigonometric.py index 9eef67357abe..2968027107eb 100644 --- a/sympy/functions/elementary/tests/test_trigonometric.py +++ b/sympy/functions/elementary/tests/test_trigonometric.py @@ -309,9 +309,8 @@ def test_tan_subs(): x,y = symbols('x,y') assert tan(x).subs(tan(x), y) == y assert tan(x).subs(x, y) == tan(y) - assert tan(x).subs(x, S.Pi/2) == S.NaN - assert tan(x).subs(x, 3*S.Pi/2) == S.NaN - + assert tan(x).subs(x, S.Pi/2) == zoo + assert tan(x).subs(x, 3*S.Pi/2) == zoo def test_cot(): x, y = symbols('x,y') @@ -384,8 +383,8 @@ def test_cot_subs(): x,y = symbols('x,y') assert cot(x).subs(cot(x), y) == y assert cot(x).subs(x, y) == cot(y) - assert cot(x).subs(x, 0) == S.NaN - assert cot(x).subs(x, S.Pi) == S.NaN + assert cot(x).subs(x, 0) == zoo + assert cot(x).subs(x, S.Pi) == zoo def test_asin(): x = Symbol('x') diff --git a/sympy/functions/elementary/trigonometric.py b/sympy/functions/elementary/trigonometric.py index e648b2c46032..498e54c692b1 100644 --- a/sympy/functions/elementary/trigonometric.py +++ b/sympy/functions/elementary/trigonometric.py @@ -759,15 +759,6 @@ def _eval_is_bounded(self): if arg.is_imaginary: return True - def _eval_subs(self, old, new): - if self == old: - return new - arg = self.args[0] - argnew = arg.subs(old, new) - if arg != argnew and (argnew/(S.Pi/2)).is_odd: - return S.NaN - return tan(argnew) - def _sage_(self): import sage.all as sage return sage.tan(self.args[0]._sage_()) @@ -935,15 +926,6 @@ def _eval_as_leading_term(self, x): def _eval_is_real(self): return self.args[0].is_real - def _eval_subs(self, old, new): - if self == old: - return new - arg = self.args[0] - argnew = arg.subs(old, new) - if arg != argnew and (argnew/S.Pi).is_integer: - return S.NaN - return cot(argnew) - def _sage_(self): import sage.all as sage return sage.cot(self.args[0]._sage_()) @@ -1421,7 +1403,7 @@ def eval(cls, y, x): elif x.is_zero: if sign_y.is_Number: return sign_y * S.Pi/2 - else: + elif x.is_zero is False: abs_yx = C.Abs(y/x) if sign_y.is_Number and abs_yx.is_number: phi = C.atan(abs_yx) diff --git a/sympy/functions/special/delta_functions.py b/sympy/functions/special/delta_functions.py index 4a834e45482c..991858cba5d8 100644 --- a/sympy/functions/special/delta_functions.py +++ b/sympy/functions/special/delta_functions.py @@ -1,6 +1,7 @@ from sympy.core import S, sympify, diff from sympy.core.function import Function, ArgumentIndexError from sympy.polys.polyerrors import PolynomialError +from sympy.functions.elementary.complexes import im ############################################################################### ################################ DELTA FUNCTION ############################### @@ -218,6 +219,8 @@ def eval(cls, arg): arg = sympify(arg) if arg is S.NaN: return S.NaN + elif im(arg).is_nonzero: + raise ValueError("Function defined only for Real Values. Complex part: %s found in %s ." % (repr(im(arg)), repr(arg)) ) elif arg.is_negative: return S.Zero elif arg.is_zero: diff --git a/sympy/functions/special/gamma_functions.py b/sympy/functions/special/gamma_functions.py index 7a94bcff5221..2a5cd5b999fe 100644 --- a/sympy/functions/special/gamma_functions.py +++ b/sympy/functions/special/gamma_functions.py @@ -2,6 +2,7 @@ from sympy.core.function import Function, ArgumentIndexError from zeta_functions import zeta from error_functions import erf +from sympy.core import Dummy,Rational from sympy.functions.elementary.exponential import log from sympy.functions.elementary.integers import floor from sympy.functions.elementary.miscellaneous import sqrt @@ -70,6 +71,12 @@ def _eval_expand_func(self, deep=True, **hints): arg = self.args[0].expand(deep, **hints) else: arg = self.args[0] + if arg.is_Rational: + if abs(arg.p) > arg.q: + x = Dummy('x') + n = arg.p // arg.q + p = arg.p - n*arg.q + return gamma(x + n).expand(func=True).subs(x, Rational(p, arg.q)) if arg.is_Add: coeff, tail = arg.as_coeff_add() diff --git a/sympy/functions/special/tests/test_delta_functions.py b/sympy/functions/special/tests/test_delta_functions.py index 31bd29d9edd2..0834abfb1381 100644 --- a/sympy/functions/special/tests/test_delta_functions.py +++ b/sympy/functions/special/tests/test_delta_functions.py @@ -4,6 +4,8 @@ from sympy.core.function import ArgumentIndexError +from sympy.core import I + x,y = symbols('x y') def test_DiracDelta(): @@ -42,5 +44,9 @@ def test_heaviside(): assert Heaviside(nan) == nan assert Heaviside(x).diff(x) == DiracDelta(x) + assert Heaviside(x+I).is_Function + assert Heaviside(I*x).is_Function raises(ArgumentIndexError, 'Heaviside(x).fdiff(2)') + raises(ValueError, 'Heaviside(I)') + raises(ValueError, 'Heaviside(2+3*I)') diff --git a/sympy/functions/special/tests/test_gamma_functions.py b/sympy/functions/special/tests/test_gamma_functions.py index 2ed46861487a..0d8fd17a8faf 100644 --- a/sympy/functions/special/tests/test_gamma_functions.py +++ b/sympy/functions/special/tests/test_gamma_functions.py @@ -34,6 +34,12 @@ def test_gamma(): assert gamma(Rational(-15, 2)) == Rational(256, 2027025)*sqrt(pi) + assert gamma(Rational(-11, 8)).expand(func=True) == Rational(64, 33)*gamma(Rational(5, 8)) + assert gamma(Rational(-10, 3)).expand(func=True) == Rational(81, 280)*gamma(Rational(2, 3)) + assert gamma(Rational(14, 3)).expand(func=True) == Rational(880, 81)*gamma(Rational(2, 3)) + assert gamma(Rational(17, 7)).expand(func=True) == Rational(30, 49)*gamma(Rational(3, 7)) + assert gamma(Rational(19, 8)).expand(func=True) == Rational(33, 64)*gamma(Rational(3, 8)) + assert gamma(x).diff(x) == gamma(x)*polygamma(0, x) assert gamma(x - 1).expand(func=True) == gamma(x)/(x-1) diff --git a/sympy/geometry/entity.py b/sympy/geometry/entity.py index 07b94fb887c9..eb5871824e8f 100644 --- a/sympy/geometry/entity.py +++ b/sympy/geometry/entity.py @@ -7,7 +7,7 @@ """ -from sympy.core.compatibility import cmp +from sympy.core.compatibility import cmp, is_sequence from sympy.core.basic import Basic from sympy.core.sympify import sympify @@ -304,3 +304,12 @@ def __contains__(self, other): return self == other raise NotImplementedError() + def _eval_subs(self, old, new): + from sympy.geometry.point import Point + if is_sequence(old) or is_sequence(new): + old = Point(old) + new = Point(new) + return self._subs(old, new) + +from sympy.core.sympify import converter, sympify +converter[GeometryEntity] = lambda x: x diff --git a/sympy/geometry/tests/test_geometry.py b/sympy/geometry/tests/test_geometry.py index 8a83d16cf9e1..b6e9db627865 100644 --- a/sympy/geometry/tests/test_geometry.py +++ b/sympy/geometry/tests/test_geometry.py @@ -531,7 +531,6 @@ def test_ellipse(): ans = [Point(a - c/8, a/2 + c), Point(a + c/8, a/2 - c)] assert e1.intersection(e2) == ans e2 = Ellipse(Point(x, y), 4, 8) - ans = list(reversed(ans)) assert [p.subs({x: 2, y:1}) for p in e1.intersection(e2)] == ans # Combinations of above @@ -844,6 +843,11 @@ def test_subs(): assert 'y' in str(o.subs(x, y)) assert p.subs({x: 1}) == Point(1, 2) assert Point(1, 2).subs(Point(1, 2), Point(3, 4)) == Point(3, 4) + assert Point(1, 2).subs((1,2), Point(3,4)) == Point(3, 4) + assert Point(1, 2).subs(Point(1,2), Point(3,4)) == Point(3, 4) + assert Point(1, 2).subs(set([(1, 2)])) == Point(2, 2) + raises(ValueError, 'Point(1, 2).subs(1)') + raises(ValueError, 'Point(1, 1).subs((Point(1, 1), Point(1, 2)), 1, 2)') def test_encloses(): # square with a dimpled left side diff --git a/sympy/integrals/integrals.py b/sympy/integrals/integrals.py index 7ca1d102a071..9b37e2a1950e 100644 --- a/sympy/integrals/integrals.py +++ b/sympy/integrals/integrals.py @@ -64,6 +64,52 @@ class Integral(Expr): __slots__ = ['is_commutative'] def __new__(cls, function, *symbols, **assumptions): + """Create an unevaluated integral. + + Arguments are an integrand followed by one or more limits. + + If no limits are given and there is only one free symbol in the + expression, that symbol will be used, otherwise an error will be + raised. + + >>> from sympy import Integral + >>> from sympy.abc import x, y + >>> Integral(x) + Integral(x, x) + >>> Integral(y) + Integral(y, y) + + When limits are provided, they are interpreted as follows (using + ``x`` as though it were the variable of integration): + + (x,) or x - indefinite integral + (x, a) - "evaluate at" integral + (x, a, b) - definite integral + + Although the same integral will be obtained from an indefinite + integral and an "evaluate at" integral when ``a == x``, they + respond differently to substitution: + + >>> i = Integral(x, x) + >>> at = Integral(x, (x, x)) + >>> i.doit() == at.doit() + True + >>> i.subs(x, 1) + Integral(1, x) + >>> at.subs(x, 1) + Integral(x, (x, 1)) + + The ``as_dummy`` method can be used to see which symbols cannot be + targeted by subs: those with a preppended underscore cannot be + changed with ``subs``. (Also, the integration variables themselves -- + the first element of a limit -- can never be changed by subs.) + + >>> i.as_dummy() + Integral(x, x) + >>> at.as_dummy() + Integral(_x, (_x, x)) + + """ # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the @@ -77,10 +123,10 @@ def __new__(cls, function, *symbols, **assumptions): limits, sign = _process_limits(*symbols) else: # no symbols provided -- let's compute full anti-derivative - limits, sign = [Tuple(s) for s in function.free_symbols], 1 - - if len(limits) != 1: - raise ValueError("specify integration variables to integrate %s" % function) + free = function.free_symbols + if len(free) != 1: + raise ValueError("specify variables of integration for %s" % function) + limits, sign = [Tuple(s) for s in free], 1 while isinstance(function, Integral): # denest the integrand @@ -283,17 +329,22 @@ def as_dummy(self): """ Replace instances of the integration variables with their dummy counterparts to make clear what are dummy variables and what - are real-world symbols in an Integral. The "integral at" limit - that has a length of 1 will be explicated with its length-2 - equivalent. + are real-world symbols in an Integral. >>> from sympy import Integral >>> from sympy.abc import x, y - >>> Integral(x).as_dummy() - Integral(_x, (_x, x)) >>> Integral(x, (x, x, y), (y, x, y)).as_dummy() Integral(_x, (_x, x, _y), (_y, x, y)) + The "integral at" limit that has a length of 1 is not treated as + though the integration symbol is a dummy, but the explicit form + of length 2 does treat the integration variable as a dummy. + + >>> Integral(x, x).as_dummy() + Integral(x, x) + >>> Integral(x, (x, x)).as_dummy() + Integral(_x, (_x, x)) + If there were no dummies in the original expression, then the output of this function will show which symbols cannot be changed by subs(), those with an underscore prefix. @@ -310,7 +361,7 @@ def as_dummy(self): for i in xrange(-1, -len(limits) - 1, -1): xab = list(limits[i]) if len(xab) == 1: - xab = xab*2 + continue x = xab[0] xab[0] = x.as_dummy() for j in range(1, len(xab)): @@ -576,7 +627,7 @@ def _eval_derivative(self, sym): >>> from sympy.abc import x, y >>> i = Integral(x + y, y, (y, 1, x)) >>> i.diff(x) - Integral(x + y, (y, x)) + Integral(1, (y, y), (y, 1, x)) + Integral(x + y, (y, x)) + Integral(1, y, (y, 1, x)) >>> i.doit().diff(x) == i.diff(x).doit() True >>> i.diff(y) @@ -612,11 +663,20 @@ def _eval_derivative(self, sym): f = Integral(f, *tuple(limits)) # assemble the pieces + def _do(f, ab): + dab_dsym = diff(ab, sym) + if not dab_dsym: + return S.Zero + if isinstance(f, Integral): + limits = [(x, x) if (len(l) == 1 and l[0] == x) else l + for l in f.limits] + f = Integral(f.function, *limits) + return f.subs(x, ab)*dab_dsym rv = 0 if b is not None: - rv += f.subs(x, b)*diff(b, sym) + rv += _do(f, b) if a is not None: - rv -= f.subs(x, a)*diff(a, sym) + rv -= _do(f, a) if len(limit) == 1 and sym == x: # the dummy variable *is* also the real-world variable arg = f @@ -816,7 +876,8 @@ def _eval_nseries(self, x, n, logx): def _eval_subs(self, old, new): """ Substitute old with new in the integrand and the limits, but don't - change anything that is (or corresponds to) a variable of integration. + change anything that is (or corresponds to) a dummy variable of + integration. The normal substitution semantics -- traversing all arguments looking for matching patterns -- should not be applied to the Integrals since @@ -825,35 +886,58 @@ def _eval_subs(self, old, new): this method just makes changes in the integrand and the limits. Not all instances of a given variable are conceptually the same: the - first argument of the limit tuple and any corresponding variable in - the integrand are dummy variables while every other symbol is a symbol - that will be unchanged when the integral is evaluated. For example, in - - Integral(x + a, (a, a, b)) - - the dummy variables are shown below with angle-brackets around them and - will not be changed by this function: + first argument of the limit tuple with length greater than 1 and any + corresponding variable in the integrand are dummy variables while + every other symbol is a symbol that will be unchanged when the integral + is evaluated. For example, the dummy variables for ``i`` can be seen + as symbols with a preppended underscore: - Integral(x + , (, a, b)) + >>> from sympy import Integral + >>> from sympy.abc import a, b, x, y + >>> i = Integral(a + x, (a, a, b)) + >>> i.as_dummy() + Integral(_a + x, (_a, a, b)) If you want to change the lower limit to 1 there is no reason to prohibit this since it is not conceptually related to the integration - variable, . Nor is there reason to disallow changing the b to 1. + variable, _a. Nor is there reason to disallow changing the b to 1. If a second limit were added, however, as in: - Integral(x + a, (a, a, b), (b, 1, 2)) + >>> i = Integral(x + a, (a, a, b), (b, 1, 2)) the dummy variables become: - Integral(x + , (, a, ), (, a, b)) - - Note that the `b` of the first limit is now a dummy variable since `b` is a - dummy variable in the second limit. + >>> i.as_dummy() + Integral(_a + x, (_a, a, _b), (_b, 1, 2)) + + Note that the ``b`` of the first limit is now a dummy variable since + ``b`` is a dummy variable in the second limit. + + The "evaluate at" form of an integral allows some flexibility in how + the integral will be treated by subs: if there is no second argument, + none of the symbols matching the integration symbol are considered to + be dummy variables, but if an explicit expression is given for a limit + then the usual interpretation of the integration symbol as a dummy + symbol applies: + + >>> Integral(x).as_dummy() # implicit integration wrt x + Integral(x, x) + >>> Integral(x, x).as_dummy() + Integral(x, x) + >>> _.subs(x, 1) + Integral(1, x) + >>> i = Integral(x, (x, x)) + >>> i.as_dummy() + Integral(_x, (_x, x)) + >>> i.subs(x, 1) + Integral(x, (x, 1)) Summary: no variable of the integrand or limit can be the target of substitution if it appears as a variable of integration in a limit - positioned to the right of it. + positioned to the right of it. The only exception is for a variable + that defines an indefinite integral limit (a single symbol): that + symbol *can* be replaced in the integrand. >>> from sympy import Integral >>> from sympy.abc import a, b, c, x, y @@ -868,25 +952,18 @@ def _eval_subs(self, old, new): >>> i.subs(x, y - c) Integral(a - c + y, (a, a, 3), (b, -c + y, c)) """ - if self == old: - return new integrand, limits = self.function, self.limits old_atoms = old.free_symbols limits = list(limits) - # make limits explicit if they are to be targeted by old: - # Integral(x, x) -> Integral(x, (x, x)) if old = x - if old.is_Symbol: - for i, l in enumerate(limits): - if len(l) == 1 and l[0] == old: - limits[i] = Tuple(l[0], l[0]) - dummies = set() for i in xrange(-1, -len(limits) - 1, -1): xab = limits[i] + if len(xab) == 1: + continue if not dummies.intersection(old_atoms): limits[i] = Tuple(xab[0], - *[l.subs(old, new) for l in xab[1:]]) + *[l._subs(old, new) for l in xab[1:]]) dummies.add(xab[0]) if not dummies.intersection(old_atoms): integrand = integrand.subs(old, new) diff --git a/sympy/integrals/meijerint.py b/sympy/integrals/meijerint.py index d5cc870e63ce..9bfb6f677023 100644 --- a/sympy/integrals/meijerint.py +++ b/sympy/integrals/meijerint.py @@ -379,6 +379,8 @@ def _split_mul(f, x): else: if a.is_Pow: c, t = a.base.as_coeff_mul(x) + if t != (x,): + c, t = expand_mul(a.base).as_coeff_mul(x) if t == (x,): po *= x**a.exp fac *= unpolarify(polarify(c**a.exp, subs=False)) diff --git a/sympy/integrals/tests/test_integrals.py b/sympy/integrals/tests/test_integrals.py index ea4b9cbfd9ba..3a5ef3be7de1 100644 --- a/sympy/integrals/tests/test_integrals.py +++ b/sympy/integrals/tests/test_integrals.py @@ -396,12 +396,17 @@ def test_subs4(): def test_subs5(): e = Integral(exp(-x**2), x) + assert e.subs(x, 5) == Integral(exp(-25), x) + e = Integral(exp(-x**2), (x, x)) assert e.subs(x, 5) == Integral(exp(-x**2), (x, 5)) e = Integral(exp(-x**2), (x, -oo, oo)) assert e.subs(x, 5) == e - e = Integral(exp(-x**2+y), x) + e = Integral(exp(-x**2 + y), x) + assert e.subs(x, 5) == Integral(exp(y - 25), x) + assert e.subs(y, 5) == Integral(exp(-x**2 + 5), x) + e = Integral(exp(-x**2+y), (x, x)) assert e.subs(x, 5) == Integral(exp(y - x**2), (x, 5)) - assert e.subs(y, 5) == Integral(exp(-x**2+5), x) + assert e.subs(y, 5) == Integral(exp(-x**2 + 5), (x, x)) e = Integral(exp(-x**2+y), (y, -oo, oo), (x, -oo, oo)) assert e.subs(x, 5) == e assert e.subs(y, 5) == e @@ -422,8 +427,8 @@ def test_subs7(): assert e.subs({x:1, y:2}) == e e = Integral(sin(x) + sin(y), (x, sin(x), sin(y)), (y, 1, 2)) - assert e._eval_subs(sin(y), 1) == e - assert e._eval_subs(sin(x), 1) == Integral(sin(x) + sin(y), (x, 1, sin(y)), + assert e.subs(sin(y), 1) == e + assert e.subs(sin(x), 1) == Integral(sin(x) + sin(y), (x, 1, sin(y)), (y, 1, 2)) def test_integration_variable(): diff --git a/sympy/integrals/tests/test_meijerint.py b/sympy/integrals/tests/test_meijerint.py index 8d0f373c63c7..78912299893f 100644 --- a/sympy/integrals/tests/test_meijerint.py +++ b/sympy/integrals/tests/test_meijerint.py @@ -582,3 +582,10 @@ def test_messy(): def test_3023(): assert integrate(exp(-I*x**2), (x, -oo, oo), meijerg=True) == \ -I*sqrt(pi)*exp(I*pi/4) + +def test_3153(): + expr = 1/x/(a+b*x)**(S(1)/3) + anti = integrate(expr, x, meijerg=True) + assert not expr.has(hyper) + # XXX the expression is a mess, but actually upon differentiation and + # putting in numerical values seems to work... diff --git a/sympy/integrals/tests/test_transforms.py b/sympy/integrals/tests/test_transforms.py index 7b1a1028c5eb..ff9d58405291 100644 --- a/sympy/integrals/tests/test_transforms.py +++ b/sympy/integrals/tests/test_transforms.py @@ -306,9 +306,10 @@ def test_inverse_mellin_transform(): assert IMT(gamma(s) + gamma(s-1), s, x, (1, oo)) == (x + 1)*exp(-x)/x # test factorisation of polys - assert simplify(expand_func(IMT(1/(s**2 + 1), s, exp(-x), - (None, oo))).rewrite(sin)) \ - == sin(x)*Heaviside(1 - exp(-x)) + r = symbols('r', real=True) + assert IMT(1/(s**2 + 1), s, exp(-x), (None, oo) + ).subs(x, r).rewrite(sin).simplify() \ + == sin(r)*Heaviside(1 - exp(-r)) # test multiplicative substitution a, b = symbols('a b', positive=True) @@ -317,7 +318,8 @@ def test_inverse_mellin_transform(): assert IMT(factorial(a/b + s/b)/(a+ s), s, x, (-a, oo)) == x**a*exp(-x**b) from sympy import expand_mul - def simp_pows(expr): return expand_mul(simplify(powsimp(expr, force=True)), deep=True).replace(exp_polar, exp) # XXX ? + def simp_pows(expr): + return expand_mul(simplify(powsimp(expr, force=True)), deep=True).replace(exp_polar, exp) # XXX ? # Now test the inverses of all direct transforms tested above @@ -387,6 +389,7 @@ def mysimp(expr): / (gamma(S(1)/2 - s - a/2)*gamma(1 - 2*s + a)), s, x, (-re(a)/2, S(1)/4))) == \ exp(-I*pi*a)*cos(sqrt(x))*besselj(a, sqrt(x)*polar_lift(-1)) + # TODO this comes out as an amazing mess, but surprisingly enough mysimp is # effective ... assert powsimp(powdenest(mysimp(IMT(gamma(a + s)*gamma(S(1)/2 - s) \ @@ -403,8 +406,8 @@ def mysimp(expr): / (gamma(-a/2 + b/2 - s + 1)*gamma(a/2 - b/2 - s + 1) \ *gamma(a/2 + b/2 - s + 1)), s, x, (-(re(a) + re(b))/2, S(1)/2))) == \ - exp(-I*pi*a -I*pi*b)*besselj(a, sqrt(x)*polar_lift(-1)) \ - *besselj(b, sqrt(x)*polar_lift(-1)) + exp(-I*pi*a -I*pi*b)*besselj(a, sqrt(x)*polar_lift(-1)) \ + *besselj(b, sqrt(x)*polar_lift(-1)) # Section 8.4.20 # TODO these come out even messier, not worth testing for now @@ -465,7 +468,10 @@ def test_laplace_transform(): # test a bug in conditions processing # TODO the auxiliary condition should be recognised/simplified - assert LT(exp(t)*cos(t), t, s)[0:-1] == ((s - 1)/(s**2 - 2*s + 2), -oo) + assert LT(exp(t)*cos(t), t, s)[:-1] in [ + ((s - 1)/(s**2 - 2*s + 2), -oo), + ((s - 1)/((s - 1)**2 + 1), -oo), + ] def test_inverse_laplace_transform(): from sympy import (expand, sinh, cosh, besselj, besseli, exp_polar, diff --git a/sympy/matrices/expressions/matexpr.py b/sympy/matrices/expressions/matexpr.py index 462f4b9a6a17..03cd74a2a651 100644 --- a/sympy/matrices/expressions/matexpr.py +++ b/sympy/matrices/expressions/matexpr.py @@ -176,6 +176,14 @@ def as_mutable(self): """ return self.as_explicit().as_mutable() + def __array__(self): + from numpy import empty + a = empty(self.shape, dtype=object) + for i in range(self.rows): + for j in range(self.cols): + a[i, j] = self[i, j] + return a + def equals(self, other): """ Test elementwise equality between matrices, potentially of different @@ -220,14 +228,12 @@ def name(self): return self.args[0] def _eval_subs(self, old, new): - if self==old: - return new - else: - shape = Tuple(*self.shape).subs(old, new) - return MatrixSymbol(self.name, *shape) + # only do substitutions in shape + shape = Tuple(*self.shape)._subs(old, new) + return MatrixSymbol(self.name, *shape) def __call__(self, *args): - raise TypeError( "%s object is not callable"%self.__class__ ) + raise TypeError( "%s object is not callable" % self.__class__ ) def _entry(self, i, j): # MatMul _entry will pass us a Dummy and ask that we remember it @@ -296,8 +302,6 @@ def matrixify(expr): Calling matrixify after calling these functions will reset classes back to their matrix equivalents - - For internal use """ class_dict = {Mul:MatMul, Add:MatAdd, MatMul:MatMul, MatAdd:MatAdd, Pow:MatPow, MatPow:MatPow} diff --git a/sympy/matrices/expressions/tests/test_matrix_exprs.py b/sympy/matrices/expressions/tests/test_matrix_exprs.py index 342e6a22e619..070090bb441a 100644 --- a/sympy/matrices/expressions/tests/test_matrix_exprs.py +++ b/sympy/matrices/expressions/tests/test_matrix_exprs.py @@ -2,7 +2,7 @@ from sympy import S, symbols, Symbol, Tuple, Mul from sympy.matrices import (eye, MatrixSymbol, Transpose, Inverse, ShapeError, MatMul, Identity, BlockMatrix, BlockDiagMatrix, block_collapse, Matrix, - ZeroMatrix, MatAdd, MatPow, matrixify) + ZeroMatrix, MatAdd, MatPow, matrixify, ImmutableMatrix) def test_transpose(): n, m, l = symbols('n m l', integer=True) @@ -316,3 +316,9 @@ def test_matrixify(): B = MatrixSymbol('B', m, l) assert matrixify(n+m) == n+m assert matrixify(Mul(A,B)) == MatMul(A,B) + +def test_dense_conversion(): + X = MatrixSymbol('X', 2,2) + x00,x01,x10,x11 = symbols('X_00 X_01 X_10 X_11') + assert ImmutableMatrix(X) == ImmutableMatrix([[x00, x01], [x10, x11]]) + assert Matrix(X) == Matrix([[x00, x01], [x10, x11]]) diff --git a/sympy/matrices/matrices.py b/sympy/matrices/matrices.py index b5ced177fa60..4ad60c22b941 100644 --- a/sympy/matrices/matrices.py +++ b/sympy/matrices/matrices.py @@ -84,76 +84,88 @@ def _handle_creation_inputs(cls, *args, **kwargs): [0, 2] """ + # Matrix(Matrix(...)) if len(args) == 1 and isinstance(args[0], MatrixBase): return args[0].rows, args[0].cols, args[0].mat - self = object.__new__(cls) + # Matrix(MatrixSymbol('X', 2,2)) + if len(args) == 1 and isinstance(args[0], Basic) and args[0].is_Matrix: + return args[0].rows, args[0].cols, args[0].as_explicit().mat + + # Matrix(2, 2, lambda i,j: i+j) if len(args) == 3 and callable(args[2]): operation = args[2] - self.rows = int(args[0]) - self.cols = int(args[1]) - self.mat = [] - for i in range(self.rows): - for j in range(self.cols): - self.mat.append(sympify(operation(i, j))) + rows = int(args[0]) + cols = int(args[1]) + mat = [] + for i in range(rows): + for j in range(cols): + mat.append(sympify(operation(i, j))) + + # Matrix(2, 2, [1,2,3,4]) elif len(args)==3 and is_sequence(args[2]): - self.rows=args[0] - self.cols=args[1] - mat = args[2] - if len(mat) != len(self): + rows = args[0] + cols = args[1] + mat = args[2] + if len(mat) != rows*cols: raise ValueError('List length should be equal to rows*columns') - self.mat = map(lambda i: sympify(i), mat) + mat = map(lambda i: sympify(i), mat) + + # Matrix(numpy.ones((2,2))) elif len(args) == 1: - mat = args[0] - if hasattr(mat, "__array__"): #pragma: no cover + in_mat = args[0] + if hasattr(in_mat, "__array__"): #pragma: no cover # NumPy array or matrix or some other object that implements # __array__. So let's first use this method to get a # numpy.array() and then make a python list out of it. - arr = mat.__array__() + arr = in_mat.__array__() if len(arr.shape) == 2: - self.rows, self.cols = arr.shape[0], arr.shape[1] - self.mat = map(lambda i: sympify(i), arr.ravel()) - return self.rows, self.cols, self.mat + rows, cols = arr.shape[0], arr.shape[1] + mat = map(lambda i: sympify(i), arr.ravel()) + return rows, cols, mat elif len(arr.shape) == 1: - self.rows, self.cols = 1, arr.shape[0] - self.mat = [0]*self.cols + rows, cols = 1, arr.shape[0] + mat = [0]*cols for i in xrange(len(arr)): - self.mat[i] = sympify(arr[i]) - return self.rows, self.cols, self.mat + mat[i] = sympify(arr[i]) + return rows, cols, mat else: raise NotImplementedError("SymPy supports just 1D and 2D matrices") - elif not is_sequence(mat, include=MatrixBase): - raise TypeError("Matrix constructor doesn't accept %s as input" % str(type(mat))) - mat = [] + elif not is_sequence(in_mat, include=MatrixBase): + raise TypeError("Matrix constructor doesn't accept %s as input" + % str(type(in_mat))) + in_mat = [] for row in args[0]: if isinstance(row, MatrixBase): - mat.extend(row.tolist()) + in_mat.extend(row.tolist()) else: - mat.append(row) - self.rows = len(mat) - if len(mat) != 0: - if not is_sequence(mat[0]): - self.cols = 1 - self.mat = map(lambda i: sympify(i), mat) - return self.rows, self.cols, self.mat - self.cols = len(mat[0]) + in_mat.append(row) + rows = len(in_mat) + if len(in_mat) != 0: + if not is_sequence(in_mat[0]): + cols = 1 + mat = map(lambda i: sympify(i), in_mat) + return rows, cols, mat + cols = len(in_mat[0]) else: - self.cols = 0 - self.mat = [] - for j in xrange(self.rows): - if len(mat[j]) != self.cols: + cols = 0 + mat = [] + for j in xrange(rows): + if len(in_mat[j]) != cols: raise ValueError("Input %s inconsistant to form a Matrix." % args) - for i in xrange(self.cols): - self.mat.append(sympify(mat[j][i])) + for i in xrange(cols): + mat.append(sympify(in_mat[j][i])) + + # Matrix() elif len(args) == 0: # Empty Matrix - self.rows = self.cols = 0 - self.mat = [] + rows = cols = 0 + mat = [] else: raise TypeError("Data type not understood") - return self.rows, self.cols, self.mat + return rows, cols, mat def transpose(self): """ @@ -3243,8 +3255,6 @@ class MutableMatrix(MatrixBase): @classmethod def _new(cls, *args, **kwargs): - if len(args)==1 and isinstance(args[0], MutableMatrix): - return args[0] rows, cols, mat = MatrixBase._handle_creation_inputs(*args, **kwargs) self = object.__new__(cls) self.rows = rows diff --git a/sympy/matrices/tests/test_matrices.py b/sympy/matrices/tests/test_matrices.py index 8b04159a99ac..677c331dfa3a 100644 --- a/sympy/matrices/tests/test_matrices.py +++ b/sympy/matrices/tests/test_matrices.py @@ -133,6 +133,8 @@ def test_creation(): assert ImmutableMatrix(c) == c.as_immutable() assert Matrix(ImmutableMatrix(c)) == ImmutableMatrix(c).as_mutable() + assert c is not Matrix(c) + def test_tolist(): x, y, z = symbols('x y z') lst = [[S.One,S.Half,x*y,S.Zero],[x,y,z,x**2],[y,-S.One,z*x,3]] diff --git a/sympy/physics/quantum/dagger.py b/sympy/physics/quantum/dagger.py index 96259e30a090..e8d09667bc2f 100644 --- a/sympy/physics/quantum/dagger.py +++ b/sympy/physics/quantum/dagger.py @@ -124,10 +124,6 @@ def eval(cls, arg): else: return d - def _eval_subs(self, old, new): - r = Dagger(self.args[0].subs(old, new)) - return r - def _eval_dagger(self): return self.args[0] diff --git a/sympy/physics/secondquant.py b/sympy/physics/secondquant.py index 09ee2554abd1..595ccc3a1f60 100644 --- a/sympy/physics/secondquant.py +++ b/sympy/physics/secondquant.py @@ -136,12 +136,6 @@ def eval(cls, arg): else: return d - def _eval_subs(self, old, new): - if self == old: - return new - r = Dagger(self.args[0].subs(old, new)) - return r - def _dagger_(self): return self.args[0] @@ -315,12 +309,6 @@ def __new__(cls, k): obj = Basic.__new__(cls, sympify(k), commutative=False) return obj - def _eval_subs(self, old, new): - if self == old: - return new - r = self.__class__(self.args[0].subs(old, new)) - return r - @property def state(self): """ @@ -911,13 +899,6 @@ def __new__(cls, occupations): obj = Basic.__new__(cls, Tuple(*occupations), commutative=False) return obj - def _eval_subs(self, old, new): - if self == old: - return new - r = self.__class__([o.subs(old, new) for o in self.args[0]]) - return r - - def __getitem__(self, i): i = int(i) return self.args[0][i] @@ -1385,12 +1366,6 @@ def ket(self): """Returns the ket part of the state""" return self.args[1] - def _eval_subs(self, old, new): - if self == old: - return new - r = self.__class__(self.bra.subs(old,new), self.ket.subs(old,new)) - return r - def __repr__(self): sbra = repr(self.bra) sket = repr(self.ket) @@ -2009,17 +1984,6 @@ def _expand_operators(self): """ return NO(self._remove_brackets) - - def _eval_subs(self,old,new): - if self == old: - return new - ops = self.args[0].args - for i in range(len(ops)): - if ops[i] == old: - l1 = ops[:i]+(new,)+ops[i+1:] - return self.__class__(Mul(*l1)) - return Expr._eval_subs(self,old,new) - def __getitem__(self,i): if isinstance(i,slice): indices = i.indices(len(self)) diff --git a/sympy/printing/latex.py b/sympy/printing/latex.py index 1d14393bcbe7..4e361f24d044 100644 --- a/sympy/printing/latex.py +++ b/sympy/printing/latex.py @@ -1057,6 +1057,9 @@ def _print_Interval(self, i): def _print_Union(self, u): return r" \cup ".join([self._print(i) for i in u.args]) + def _print_Intersection(self, u): + return r" \cap ".join([self._print(i) for i in u.args]) + def _print_EmptySet(self, e): return r"\emptyset" diff --git a/sympy/printing/pretty/pretty.py b/sympy/printing/pretty/pretty.py index 140ccc91b8a7..ef414dc130f1 100644 --- a/sympy/printing/pretty/pretty.py +++ b/sympy/printing/pretty/pretty.py @@ -132,7 +132,10 @@ def _print_Not(self, e): else: return self._print_Function(e) - def __print_Boolean(self, args, char): + def __print_Boolean(self, e, char, sort=True): + args = e.args + if sort: + args = sorted(e.args, key=default_sort_key) arg = args[0] pform = self._print(arg) @@ -151,47 +154,44 @@ def __print_Boolean(self, args, char): return pform def _print_And(self, e): - args = sorted(e.args, key=default_sort_key) if self._use_unicode: - return self.__print_Boolean(args, u"\u2227") + return self.__print_Boolean(e, u"\u2227") else: return self._print_Function(e) def _print_Or(self, e): - args = sorted(e.args, key=default_sort_key) if self._use_unicode: - return self.__print_Boolean(args, u"\u2228") + return self.__print_Boolean(e, u"\u2228") else: return self._print_Function(e) def _print_Xor(self, e): - args = sorted(e.args, key=default_sort_key) if self._use_unicode: - return self.__print_Boolean(args, u"\u22bb") + return self.__print_Boolean(e, u"\u22bb") else: return self._print_Function(e) def _print_Nand(self, e): if self._use_unicode: - return self.__print_Boolean(e.args, u"\u22bc") + return self.__print_Boolean(e, u"\u22bc") else: return self._print_Function(e) def _print_Nor(self, e): if self._use_unicode: - return self.__print_Boolean(e.args, u"\u22bd") + return self.__print_Boolean(e, u"\u22bd") else: return self._print_Function(e) def _print_Implies(self, e): if self._use_unicode: - return self.__print_Boolean(e.args, u"\u2192") + return self.__print_Boolean(e, u"\u2192", sort=False) else: return self._print_Function(e) def _print_Equivalent(self, e): if self._use_unicode: - return self.__print_Boolean(e.args, u"\u2261") + return self.__print_Boolean(e, u"\u2261") else: return self._print_Function(e) @@ -1129,7 +1129,7 @@ def _print_Fraction(self, expr): def _print_ProductSet(self, p): prod_char = u'\xd7' return self._print_seq(p.sets, None, None, ' %s '%prod_char, - parenthesize = lambda set:set.is_Union ) + parenthesize = lambda set:set.is_Union or set.is_Intersection) def _print_FiniteSet(self, s): if len(s) > 10: @@ -1163,12 +1163,19 @@ def _print_Interval(self, i): return self._print_seq(i.args[:2], left, right) + def _print_Intersection(self, u): + + delimiter = ' %s ' % pretty_atom('Intersection') + + return self._print_seq(u.args, None, None, delimiter, + parenthesize = lambda set:set.is_ProductSet or set.is_Union) + def _print_Union(self, u): union_delimiter = ' %s ' % pretty_atom('Union') return self._print_seq(u.args, None, None, union_delimiter, - parenthesize = lambda set:set.is_ProductSet) + parenthesize = lambda set:set.is_ProductSet or set.is_Intersection) def _print_seq(self, seq, left=None, right=None, delimiter=', ', parenthesize=lambda x: False): diff --git a/sympy/printing/pretty/pretty_symbology.py b/sympy/printing/pretty/pretty_symbology.py index f12545a916f0..e3686250e9a4 100644 --- a/sympy/printing/pretty/pretty_symbology.py +++ b/sympy/printing/pretty/pretty_symbology.py @@ -392,7 +392,8 @@ def xsym(sym): #'ImaginaryUnit' : U('MATHEMATICAL ITALIC SMALL I'), 'ImaginaryUnit' : U('DOUBLE-STRUCK ITALIC SMALL I'), 'EmptySet' : U('EMPTY SET'), - 'Union' : U('UNION') + 'Union' : U('UNION'), + 'Intersection' : U('INTERSECTION') } def pretty_atom(atom_name, default=None): diff --git a/sympy/printing/pretty/tests/test_pretty.py b/sympy/printing/pretty/tests/test_pretty.py index c5059ca96a5d..2fc0b9a02d3f 100644 --- a/sympy/printing/pretty/tests/test_pretty.py +++ b/sympy/printing/pretty/tests/test_pretty.py @@ -2628,11 +2628,22 @@ def test_pretty_Boolean(): assert pretty(expr) == "Implies(x, y)" assert upretty(expr) == u"x → y" + # don't sort args + expr = Implies(y, x, evaluate=False) + + assert pretty(expr) == "Implies(y, x)" + assert upretty(expr) == u"y → x" + expr = Equivalent(x, y, evaluate=False) assert pretty(expr) == "Equivalent(x, y)" assert upretty(expr) == u"x ≡ y" + expr = Equivalent(y, x, evaluate=False) + + assert pretty(expr) == "Equivalent(y, x)" + assert upretty(expr) == u"x ≡ y" + def test_pretty_Domain(): expr = FF(23) diff --git a/sympy/printing/tests/test_str.py b/sympy/printing/tests/test_str.py index 4aa838e36d09..f90fc2bf7e84 100644 --- a/sympy/printing/tests/test_str.py +++ b/sympy/printing/tests/test_str.py @@ -4,7 +4,7 @@ factorial, factorial2, Float, Function, GoldenRatio, I, Integer, Integral, Interval, Lambda, Limit, log, Matrix, nan, O, oo, pi, Piecewise, Rational, Rel,S, sin, SparseMatrix, sqrt, summation, Sum, Symbol, symbols, Wild, - WildFunction, zeta, zoo, Dummy, Dict, Tuple, round) + WildFunction, zeta, zoo, Dummy, Dict, Tuple) from sympy.core import Expr from sympy.physics.units import second, joule from sympy.polys import Poly, RootOf, RootSum, groebner @@ -310,8 +310,8 @@ def test_Float(): assert str(pi.evalf(1+2)) == '3.14' assert str(pi.evalf(1+14)) == '3.14159265358979' assert str(pi.evalf(1+64)) == '3.1415926535897932384626433832795028841971693993751058209749445923' - assert str(round(pi, -1)) == '0.' - assert str((pi**400 - round(pi**400, 1)).n(1)) == '-0.e+91' + assert str(pi.round(-1)) == '0.' + assert str((pi**400 - (pi**400).round(1)).n(1)) == '-0.e+91' def test_Relational(): assert str(Rel(x, y, "<")) == "x < y" diff --git a/sympy/series/order.py b/sympy/series/order.py index fb409f500e24..cb8315d171f1 100644 --- a/sympy/series/order.py +++ b/sympy/series/order.py @@ -237,14 +237,12 @@ def _contains(self, expr): return self.contains(obj) def _eval_subs(self, old, new): - if self == old: - return new - if isinstance(old, Symbol) and old in self.variables: + if old.is_Symbol and old in self.variables: i = list(self.variables).index(old) if isinstance(new, Symbol): - return Order(self.expr._eval_subs(old, new), *(self.variables[:i]+(new,)+self.variables[i+1:])) - return Order(self.expr._eval_subs(old, new), *(self.variables[:i]+self.variables[i+1:])) - return Order(self.expr._eval_subs(old, new), *self.variables) + return Order(self.expr._subs(old, new), *(self.variables[:i]+(new,)+self.variables[i+1:])) + return Order(self.expr._subs(old, new), *(self.variables[:i]+self.variables[i+1:])) + return Order(self.expr._subs(old, new), *self.variables) def _eval_derivative(self, x): return self.func(self.expr.diff(x), *self.variables) or self diff --git a/sympy/series/tests/test_limits.py b/sympy/series/tests/test_limits.py index ed0385616515..0b7298bedc5c 100644 --- a/sympy/series/tests/test_limits.py +++ b/sympy/series/tests/test_limits.py @@ -152,6 +152,7 @@ def test_issue772(): def test_exponential(): n = Symbol('n') + x = Symbol('x', real=True) assert limit((1+x/n)**n,n,oo) == exp(x) assert limit((1+x/(2*n))**n,n,oo) == exp(x/2) assert limit((1+x/(2*n+1))**n,n,oo) == exp(x/2) diff --git a/sympy/simplify/cse_main.py b/sympy/simplify/cse_main.py index 3398ccee2968..0933291b2f7f 100644 --- a/sympy/simplify/cse_main.py +++ b/sympy/simplify/cse_main.py @@ -137,6 +137,8 @@ def insert(subtree): to_eliminate.insert(index_to_insert, subtree) for expr in reduced_exprs: + if not isinstance(expr, Basic): + continue for e in expr.as_numer_denom() if not expr.is_Add else [expr]: pt = preorder_traversal(e) for subtree in pt: diff --git a/sympy/simplify/cse_opts.py b/sympy/simplify/cse_opts.py index c7c13b547417..a74c5b1eefb5 100644 --- a/sympy/simplify/cse_opts.py +++ b/sympy/simplify/cse_opts.py @@ -1,7 +1,7 @@ """ Optimizations of the expression tree representation for better CSE opportunities. """ -from sympy.core import Add, Mul, Expr, S +from sympy.core import Add, Basic, Expr, Mul, S from sympy.core.exprtools import factor_terms from sympy.utilities.iterables import preorder_traversal @@ -20,9 +20,10 @@ def sub_pre(e): e = e.subs([(a, Mul(-1, -a, evaluate=False) if adds[a] else a) for a in adds]) # now replace any persisting Adds, a, that can have -1 extracted with Neg(-a) - reps = dict([(a, Neg(-a)) for a in e.atoms(Add) - if adds.get(a, a.could_extract_minus_sign())]) - e = e.xreplace(reps) + if isinstance(e, Basic): + reps = dict([(a, Neg(-a)) for a in e.atoms(Add) + if adds.get(a, a.could_extract_minus_sign())]) + e = e.xreplace(reps) return e def sub_post(e): diff --git a/sympy/simplify/simplify.py b/sympy/simplify/simplify.py index 59c522fee24c..f6b2ddb94bb2 100644 --- a/sympy/simplify/simplify.py +++ b/sympy/simplify/simplify.py @@ -162,7 +162,7 @@ def separate(expr, deep=False, force=False): 2**y*sin(x)**y + 2**y*cos(x)**y >>> separate((2*exp(y))**x) - 2**x*exp(x*y) + 2**x*exp(y)**x >>> separate((2*cos(x))**y) 2**y*cos(x)**y @@ -1879,8 +1879,7 @@ def bkey(b, e=None): x**3/2 -> (x, 2), 3 x**y -> (x**y, 1), 1 x**(2*y/3) -> (x**y, 3), 2 - - >>> x+2 + exp(x/2) -> (exp(a), 2), 1 ''' if e is not None: # coming from c_powers or from below @@ -1993,7 +1992,8 @@ def update(b): # there may be terms still in common_b that were bases that were # identified as needing processing, so remove those, too for (b, q), e in common_b.items(): - if b.is_Pow and q is not S.One and not b.exp.is_Rational: + if (b.is_Pow or b.func is exp) and \ + q is not S.One and not b.exp.is_Rational: b, be = b.as_base_exp() b = b**(be/q) else: diff --git a/sympy/simplify/tests/test_cse.py b/sympy/simplify/tests/test_cse.py index beb7f1f61d0c..851351ac8de1 100644 --- a/sympy/simplify/tests/test_cse.py +++ b/sympy/simplify/tests/test_cse.py @@ -1,7 +1,7 @@ import itertools from sympy import (Add, Mul, Pow, Symbol, exp, sqrt, symbols, sympify, cse, - Matrix, S, sin) + Matrix, S, sin, Eq) from sympy.simplify import cse_main, cse_opts from sympy.utilities.pytest import XFAIL @@ -132,3 +132,7 @@ def test_issue_921(): def test_issue_1104(): assert cse(sin(x**x)/x**x) == ([(x0, x**x)], [sin(x0)/x0]) + +def test_issue_3164(): + e = Eq(x*(-x + 1) + x*(x - 1), 0) + assert cse(e) == ([], [True]) diff --git a/sympy/simplify/tests/test_simplify.py b/sympy/simplify/tests/test_simplify.py index d06b6ead1011..647dfa606b88 100644 --- a/sympy/simplify/tests/test_simplify.py +++ b/sympy/simplify/tests/test_simplify.py @@ -274,7 +274,7 @@ def test_separate(): assert separate((exp((x*y)**z)*exp(y))**2, deep=True, force=True) == exp(2*x**z*y**z)*exp(2*y) assert separate((exp(x)*exp(y))**z).is_Pow - assert separate((exp(x)*exp(y))**z, force=True) == exp(x*z)*exp(y*z) + assert separate((exp(x)*exp(y))**z, force=True) == exp(x)**z*exp(y)**z def test_powsimp(): x, y, z, n = symbols('x,y,z,n') @@ -331,6 +331,8 @@ def test_powsimp(): assert powsimp(eq).exp == eq.exp == 2*a/3 # eq != (x**a)**(2/3) (try x = -1 and a = 3 to see) assert powsimp(2**(2*x)) == 4**x # powdenest goes the other direction + assert powsimp(exp(p/2)) == exp(p/2) + def test_powsimp_polar(): from sympy import polar_lift, exp_polar x, y, z = symbols('x y z') @@ -722,7 +724,7 @@ def test_powdenest(): assert powdenest(x) == x assert powdenest(x + 2*(x**(2*a/3))**(3*x)) == (x + 2*(x**(2*a/3))**(3*x)) - assert powdenest((exp(2*a/3))**(3*x)) == (exp(a/3))**(6*x) + assert powdenest((exp(2*a/3))**(3*x)) # -X-> (exp(a/3))**(6*x) assert powdenest((x**(2*a/3))**(3*x)) == ((x**(2*a/3))**(3*x)) assert powdenest(exp(3*x*log(2))) == 2**(3*x) assert powdenest(sqrt(p**2)) == p diff --git a/sympy/solvers/tests/test_ode.py b/sympy/solvers/tests/test_ode.py index 66c2fbb1e949..e1755387ee47 100644 --- a/sympy/solvers/tests/test_ode.py +++ b/sympy/solvers/tests/test_ode.py @@ -4,8 +4,8 @@ from sympy import (acos, acosh, asinh, atan, cos, Derivative, diff, dsolve, Eq, erf, exp, Function, I, Integral, LambertW, log, O, pi, Rational, RootOf, S, simplify, sin, sqrt, Symbol, tan, asin, - Piecewise) -from sympy.abc import x, y, z + Piecewise, symbols) +x,y,z = symbols('x:z', real=True) from sympy.solvers.ode import (_undetermined_coefficients_match, checkodesol, classify_ode, constant_renumber, constantsimp, homogeneous_order, ode_order) @@ -420,14 +420,15 @@ def test_separable5(): ((1 - x)*(-x*exp(x) + exp(x)))) sol19e = Eq(f(x), (C1*(1 - x) - x*(-x*exp(x) + exp(x)) - x*exp(x) + exp(x))/((1 - x)*(-exp(x) + x*exp(x)))) + sol19f = Eq(f(x), (C1 + (x - 1)*exp(x))*exp(-x)/(-x + 1)) sol20 = Eq(log(-1 + 3*f(x)**2)/6, C1 + x**2/2) sol21 = Eq(-exp(-f(x)), C1 + exp(x)) assert dsolve(eq15, hint='separable') == sol15 assert dsolve(eq16, hint='separable', simplify=False) == sol16 assert dsolve(eq17, hint='separable') == sol17 assert dsolve(eq18, hint='separable', simplify=False) == sol18 - assert dsolve(eq19, hint='separable') in [sol19a, sol19b, sol19c, - sol19d, sol19e] + assert dsolve(eq19, hint='separable') in [sol19f, sol19a, sol19b, sol19c, + sol19d, sol19e] assert dsolve(eq20, hint='separable', simplify=False) == sol20 assert dsolve(eq21, hint='separable', simplify=False) == sol21 assert checkodesol(eq15, sol15, order=1, solve_for_func=False)[0] @@ -1165,7 +1166,8 @@ def test_Liouville_ODE(): sol1a = Eq(C1 + C2/x - exp(-f(x)), 0) sol2 = sol1 sol3 = set([Eq(f(x), -sqrt(C1 + C2*log(x))), Eq(f(x), sqrt(C1 + C2*log(x)))]) - sol4 = set([Eq(f(x), -sqrt(2)*sqrt(C1 + C2*exp(-x))), Eq(f(x), sqrt(2)*sqrt(C1 + C2*exp(-x)))]) + sol4 = set([Eq(f(x), sqrt(C1 + C2*exp(x))*exp(-x/2)), + Eq(f(x), -sqrt(C1 + C2*exp(x))*exp(-x/2))]) sol5 = Eq(f(x), log(C1 + C2/x)) sol1s = constant_renumber(sol1, 'C', 1, 2) sol2s = constant_renumber(sol2, 'C', 1, 2) @@ -1176,7 +1178,7 @@ def test_Liouville_ODE(): assert dsolve(eq1a, hint=hint) in (sol1, sol1s) assert dsolve(eq2, hint=hint) in (sol2, sol2s) assert set(dsolve(eq3, hint=hint)) in (sol3, sol3s) - assert set(dsolve(eq4, hint=hint)) in (sol4, sol4s) # XXX: remove sqrt(2) factor + assert set(dsolve(eq4, hint=hint)) in (sol4, sol4s) assert dsolve(eq5, hint=hint) in (sol5, sol5s) assert checkodesol(eq1, sol1, order=2, solve_for_func=False)[0] assert checkodesol(eq1a, sol1a, order=2, solve_for_func=False)[0] diff --git a/sympy/solvers/tests/test_solvers.py b/sympy/solvers/tests/test_solvers.py index 4539d7e996e0..297537c7bd43 100644 --- a/sympy/solvers/tests/test_solvers.py +++ b/sympy/solvers/tests/test_solvers.py @@ -400,8 +400,11 @@ def test_issue_1694(): assert solve(1/(5 + x)**(S(1)/5) - 9, x) == [-295244/S(59049)] assert solve(sqrt(x) + sqrt(sqrt(x)) - 4) == [-9*sqrt(17)/2 + 49*S.Half] - assert solve(Poly(sqrt(exp(x)) + sqrt(exp(-x)) - 4)) == \ - [2*log(-sqrt(3) + 2), 2*log(sqrt(3) + 2)] + assert solve(Poly(sqrt(exp(x)) + sqrt(exp(-x)) - 4)) in \ + [ + [2*log(-sqrt(3) + 2), 2*log(sqrt(3) + 2)], + [log(-4*sqrt(3) + 7), log(4*sqrt(3) + 7)], + ] assert solve(Poly(exp(x) + exp(-x) - 4)) == [log(-sqrt(3) + 2), log(sqrt(3) + 2)] assert solve(x**y + x**(2*y) - 1, x) == \ [(-S.Half + sqrt(5)/2)**(1/y), (-S.Half - sqrt(5)/2)**(1/y)] @@ -635,14 +638,14 @@ def s_check(rv, ans): raises(ValueError, 'unrad(sqrt(x) + sqrt(x+1) + sqrt(1-sqrt(x)) + 3)') raises(ValueError, 'unrad(sqrt(x) + (x+1)**Rational(1,3) + 2*sqrt(y))') # same as last but consider only y - assert check(unrad(sqrt(x) + (x+1)**Rational(1,3) + 2*sqrt(y), y), + assert check(unrad(sqrt(x) + (x + 1)**Rational(1,3) + 2*sqrt(y), y), (4*y - (sqrt(x) + (x + 1)**(S(1)/3))**2, [], [])) - assert check(unrad(sqrt(x/(1 - x)) + (x+1)**Rational(1,3)), + assert check(unrad(sqrt(x/(1 - x)) + (x + 1)**Rational(1,3)), (x**3/(-x + 1)**3 - (x + 1)**2, [], [(-x + 1)**3])) # same as last but consider only y; no y-containing denominators now assert s_check(unrad(sqrt(x/(1 - x)) + 2*sqrt(y), y), (x/(-x + 1) - 4*y, [], [])) - assert check(unrad(sqrt(x)*sqrt(1-x) + 2, x), + assert check(unrad(sqrt(x)*sqrt(1 - x) + 2, x), (x*(-x + 1) - 4, [], [])) # http://tutorial.math.lamar.edu/