From b99ea96429cb40611628521bad403798fef2d75b Mon Sep 17 00:00:00 2001 From: Bharath M R Date: Fri, 2 Mar 2012 21:27:48 +0530 Subject: [PATCH 001/104] Faster inversion function using merge sort fixed a minor bug in the algorithm added missing spaces removed trailing spaces added documentation for inversion and fixed the ref link changed the recursive function in inversion to iterative added tests for inversion of large numbers changed the n value for algorithm switch and included the faster code by pernici changed the u variable to right to make it more meaningful changed the u variable to right to make it more meaningful edited the documentation replaced the complicated doctest basic changes --- sympy/combinatorics/permutations.py | 67 +++++++++++++++++-- .../combinatorics/tests/test_permutations.py | 1 + 2 files changed, 63 insertions(+), 5 deletions(-) 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_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 From e9efbe7963dcff689547644e648da99486fc8bbf Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Mon, 12 Mar 2012 15:04:08 +0530 Subject: [PATCH 002/104] Change coverage_doctest.py implementation coverage_doctest.py currently uses a parsing based approach to get list of files in a module(folder) and then parse each file to find occurances of methods/functions. This has an obvious flaw that parsing is ambiguous. Hence the plan is to change the current implementation to reading a module and getting list of methods/classes/functions/and what not using __import__, __doc__ ,inspect. --- bin/coverage_doctest.py | 167 +++++++++++++++------------------------- 1 file changed, 62 insertions(+), 105 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index ea4320494323..20acacf28595 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,118 +20,78 @@ import os import re import sys +import string +import inspect from optparse import OptionParser -def parse_file(file, verbose=False): +def coverage(module_path, verbose=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: + # Most likely cause, absence of __init__ + print module_path + ' could not be loaded!' + return 0, 0 + + # Get the list of members (currently everything possible) + m_members = inspect.getmembers(m) + + # Create a list of class and definitions + m_classes = filter(lambda x: inspect.isclass(x[1]), m_members) + m_functions = filter(lambda x: inspect.isfunction(x[1]), m_members) + + # Iterate over functions first + print module_path + print '-'*70 + 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) - 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 - 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' - - print "SCORE %s: %s%% (%s of %s)" % (filename, score, doctests, num_functions) - print '-'*70 + for c in m_classes: + + class_name, class_obj = c[0], c[1] + + # Check if the class needs to be skipped + skip = False + filename = None - return len(has_doctest), num_functions + # Removes the built-in types, a bit hacky + try: + filename = inspect.getfile(c[1]) + except TypeError as (strerror): + #print 'ERRORR:' + str(strerror) + skip = True + # If import imported something other than our module + if inspect.getmodule(class_obj).__name__ != module_path: + skip = True + if skip or class_name.startswith('_') or \ + not 'sympy' in filename: + skip = True + if skip: + skipped.append(c) + + # Check if the class has docstrings + if not skip and not class_obj.__doc__: + missing_docstring.append(c) + + + print 'Missing: '+str(missing_docstring) + return 0, 0 def go(file, verbose=False, exact=True): + if os.path.isdir(file): doctests, num_functions = 0, 0 for F in os.listdir(file): @@ -143,16 +100,16 @@ def go(file, verbose=False, exact=True): 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) + return coverage(string.replace(file,'/','.')[:-3], verbose) 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/ From 09ffd10e0c6af74cbd3ca792daf31d3192884d2b Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Mon, 12 Mar 2012 15:34:55 +0530 Subject: [PATCH 003/104] Modularize code and format output for classes I have taken the following steps to improve readibility of code: * Create a new function, _is_skip which can handle if an object (class/def) should be skipped for consideration. * Format the output for classes into Missing Docstrings and Missing Doctests. The current test for Doctests is as same as before, if we find a string '>>>' inside the docstring, we say it has a doctest otherwise not. --- bin/coverage_doctest.py | 83 ++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 20acacf28595..feb61fa8633c 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -24,6 +24,33 @@ import inspect from optparse import OptionParser +def _is_skip(module_path, name, obj): + """ Given a module object and the type object, the function + determines if the objected should be ignored on following + remarks: + * It is a builtin + * Starts with an underscore (is private) + """ + + skip = False + filename = None + + # Removes the built-in types, a bit hacky + try: + filename = inspect.getfile(obj) + except TypeError as (strerror): + skip = True + + # If import imported something other than our module + if inspect.getmodule(obj).__name__ != module_path: + skip = True + + if skip or name.startswith('_') or \ + not 'sympy' in filename: + skip = True + + return skip + def coverage(module_path, verbose=False): """ Given a module path, builds an index of all classes and functions @@ -48,46 +75,52 @@ def coverage(module_path, verbose=False): m_functions = filter(lambda x: inspect.isfunction(x[1]), m_members) # Iterate over functions first + print + print '='*70 print module_path - print '-'*70 + print '='*70 skipped = [] missing_docstring = [] missing_doctest = [] has_doctest = [] indirect_doctest = [] + classes = 0 + print + print 'CLASSES' + print '-'*10 for c in m_classes: - class_name, class_obj = c[0], c[1] - # Check if the class needs to be skipped - skip = False - filename = None - - # Removes the built-in types, a bit hacky - try: - filename = inspect.getfile(c[1]) - except TypeError as (strerror): - #print 'ERRORR:' + str(strerror) - skip = True - - # If import imported something other than our module - if inspect.getmodule(class_obj).__name__ != module_path: - skip = True - - if skip or class_name.startswith('_') or \ - not 'sympy' in filename: - skip = True - if skip: - skipped.append(c) - + skip = _is_skip(module_path, class_name, class_obj) + if skip: skipped.append(c) + else: classes = classes + 1 # Check if the class has docstrings if not skip and not class_obj.__doc__: missing_docstring.append(c) + # Check for a docstring + if not skip and class_obj.__doc__ and \ + not '>>>' in class_obj.__doc__: + missing_doctest.append(c) + + if not classes: + print + print 'No classes found!\n' + else: + if missing_docstring: + print + print 'Missing docstrings' + print '*'*15 + for md in missing_docstring: + print md[0] + if missing_doctest: + print + print 'Missing doctests' + print '*'*15 + for md in missing_doctest: + print md[0] - - print 'Missing: '+str(missing_docstring) return 0, 0 def go(file, verbose=False, exact=True): From ddd06b92deac8cac7308807088b5423423f0466e Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Mon, 12 Mar 2012 16:02:16 +0530 Subject: [PATCH 004/104] Include functions in coverage; modularize printing Functions have been included in the reporting framework, though I still have to ensure that functions are skipped properly. I found the earlier implementation of skip excessive, where ONLY startswith('_') should suffice. Printing report has been made another function. In further steps I will be trying to combine function and class coverage/reporting because in essence both work simply over __doc__ strings, and their pruning mechanism looks same. --- bin/coverage_doctest.py | 110 +++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 30 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index feb61fa8633c..201c4ab50935 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -45,12 +45,56 @@ def _is_skip(module_path, name, obj): if inspect.getmodule(obj).__name__ != module_path: skip = True - if skip or name.startswith('_') or \ - not 'sympy' in filename: + if skip or name.startswith('_'): skip = True return skip +def print_coverage(c, c_md, c_mdt, f, f_md, f_mdt): + + # Print routines (can be deported?) + + print + print 'CLASSES' + print '*'*10 + + if not c: + print + print 'No classes found!\n' + else: + if c_md: + print + print 'Missing docstrings' + print '-'*15 + for md in c_md: + print md[0] + if c_mdt: + print + print 'Missing doctests' + print '-'*15 + for md in c_mdt: + print md[0] + + print + print 'DEFINITIONS' + print '*'*10 + if not f: + print + print 'No functions found!\n' + else: + if f_md: + print + print 'Missing docstrings' + print '-'*15 + for md in f_md: + print md[0] + if f_mdt: + print + print 'Missing doctests' + print '-'*15 + for md in f_mdt: + print md[0] + def coverage(module_path, verbose=False): """ Given a module path, builds an index of all classes and functions @@ -74,52 +118,58 @@ def coverage(module_path, verbose=False): m_classes = filter(lambda x: inspect.isclass(x[1]), m_members) m_functions = filter(lambda x: inspect.isfunction(x[1]), m_members) - # Iterate over functions first + print print '='*70 print module_path print '='*70 - skipped = [] - missing_docstring = [] - missing_doctest = [] - has_doctest = [] - indirect_doctest = [] + # Iterate over classes + c_skipped = [] + c_md = [] + c_mdt = [] + c_has_doctest = [] + c_indirect_doctest = [] classes = 0 - print - print 'CLASSES' - print '-'*10 for c in m_classes: class_name, class_obj = c[0], c[1] # Check if the class needs to be skipped skip = _is_skip(module_path, class_name, class_obj) - if skip: skipped.append(c) + if skip: c_skipped.append(c) else: classes = classes + 1 # Check if the class has docstrings if not skip and not class_obj.__doc__: - missing_docstring.append(c) + c_md.append(c) # Check for a docstring if not skip and class_obj.__doc__ and \ not '>>>' in class_obj.__doc__: - missing_doctest.append(c) + c_mdt.append(c) - if not classes: - print - print 'No classes found!\n' - else: - if missing_docstring: - print - print 'Missing docstrings' - print '*'*15 - for md in missing_docstring: - print md[0] - if missing_doctest: - print - print 'Missing doctests' - print '*'*15 - for md in missing_doctest: - print md[0] + # Iterate over functions + f_skipped = [] + f_md = [] + f_mdt = [] + f_has_doctest = [] + f_indirect_doctest = [] + functions = 0 + + + for f in m_functions: + f_name, f_obj = f[0], f[1] + # Check if the class needs to be skipped + skip = _is_skip(module_path, f_name, f_obj) + if skip: f_skipped.append(f) + else: functions = functions + 1 + # Check if the class has docstrings + if not skip and not f_obj.__doc__: + f_md.append(f) + # Check for a docstring + if not skip and f_obj.__doc__ and \ + not '>>>' in f_obj.__doc__: + f_mdt.append(f) + + print_coverage(classes, c_md, c_mdt, functions, f_md, f_mdt) return 0, 0 From 66859ab01cc4c27e4bc7ac891f392775d954d65c Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 02:24:00 +0530 Subject: [PATCH 005/104] Use dir() builtin to find members in module; recurse on classes Using dir() builtin to find out all members of a module. Also, instead of listing functions and classes separately -- we use a single loop to check each memeber for which category it belongs to. Additionally, if it is a class we recurse to find all methods inside it. Also, the printing of output has been made prettier to reflect which class a method belongs to, functions are bare as expected. --- bin/coverage_doctest.py | 118 ++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 66 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 201c4ab50935..5bf4f79268af 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -24,32 +24,6 @@ import inspect from optparse import OptionParser -def _is_skip(module_path, name, obj): - """ Given a module object and the type object, the function - determines if the objected should be ignored on following - remarks: - * It is a builtin - * Starts with an underscore (is private) - """ - - skip = False - filename = None - - # Removes the built-in types, a bit hacky - try: - filename = inspect.getfile(obj) - except TypeError as (strerror): - skip = True - - # If import imported something other than our module - if inspect.getmodule(obj).__name__ != module_path: - skip = True - - if skip or name.startswith('_'): - skip = True - - return skip - def print_coverage(c, c_md, c_mdt, f, f_md, f_mdt): # Print routines (can be deported?) @@ -67,13 +41,13 @@ def print_coverage(c, c_md, c_mdt, f, f_md, f_mdt): print 'Missing docstrings' print '-'*15 for md in c_md: - print md[0] + print md if c_mdt: print print 'Missing doctests' print '-'*15 for md in c_mdt: - print md[0] + print md print print 'DEFINITIONS' @@ -87,13 +61,13 @@ def print_coverage(c, c_md, c_mdt, f, f_md, f_mdt): print 'Missing docstrings' print '-'*15 for md in f_md: - print md[0] + print md if f_mdt: print print 'Missing doctests' print '-'*15 for md in f_mdt: - print md[0] + print md def coverage(module_path, verbose=False): @@ -112,19 +86,14 @@ def coverage(module_path, verbose=False): return 0, 0 # Get the list of members (currently everything possible) - m_members = inspect.getmembers(m) - - # Create a list of class and definitions - m_classes = filter(lambda x: inspect.isclass(x[1]), m_members) - m_functions = filter(lambda x: inspect.isfunction(x[1]), m_members) - + m_members = dir(m) print print '='*70 print module_path print '='*70 - # Iterate over classes + c_skipped = [] c_md = [] c_mdt = [] @@ -132,21 +101,6 @@ def coverage(module_path, verbose=False): c_indirect_doctest = [] classes = 0 - for c in m_classes: - class_name, class_obj = c[0], c[1] - # Check if the class needs to be skipped - skip = _is_skip(module_path, class_name, class_obj) - if skip: c_skipped.append(c) - else: classes = classes + 1 - # Check if the class has docstrings - if not skip and not class_obj.__doc__: - c_md.append(c) - # Check for a docstring - if not skip and class_obj.__doc__ and \ - not '>>>' in class_obj.__doc__: - c_mdt.append(c) - - # Iterate over functions f_skipped = [] f_md = [] f_mdt = [] @@ -154,20 +108,52 @@ def coverage(module_path, verbose=False): f_indirect_doctest = [] functions = 0 - - for f in m_functions: - f_name, f_obj = f[0], f[1] - # Check if the class needs to be skipped - skip = _is_skip(module_path, f_name, f_obj) - if skip: f_skipped.append(f) - else: functions = functions + 1 - # Check if the class has docstrings - if not skip and not f_obj.__doc__: - f_md.append(f) - # Check for a docstring - if not skip and f_obj.__doc__ and \ - not '>>>' in f_obj.__doc__: - f_mdt.append(f) + for member in m_members: + # Identify if the member (class/def) a part of this module + obj = getattr(m, member) + obj_mod = inspect.getmodule(obj) + if not obj_mod or not obj_mod.__name__ == module_path: + continue + + # If it's a function, simply process it + if inspect.isfunction(obj) or inspect.ismethod(obj): + functions = functions + 1 + # Various scenarios + if member.startswith('_'): f_skipped.append(member) + elif not obj.__doc__: f_md.append(member) + elif not '>>>' in obj.__doc__: f_mdt.append(member) + + # If it's a class, look at it's methods too + elif inspect.isclass(obj): + #print 'Class: '+member + classes = classes + 1 + # Process the class first + if not obj.__doc__: c_md.append(member) + elif not '>>>' in obj.__doc__: c_mdt.append(member) + + # Iterate through it's members + for class_m in dir(obj): + + # Check if the method is a part of this module + class_m_mod = None + class_m_obj = None + + # Gutsy hack; need to expand reasons + try: + class_m_obj = getattr(obj, class_m) + class_m_mod = inspect.getmodule(class_m_obj) + except: + continue + + if not class_m_mod or not class_m_obj or \ + not class_m_mod.__name__ == module_path: + continue + + # Check function for various categories + full_name = member + '.' + class_m + if class_m.startswith('_'): f_skipped.append(full_name) + elif not class_m_obj.__doc__: f_md.append(full_name) + elif not '>>>' in class_m_obj.__doc__: f_mdt.append(full_name) print_coverage(classes, c_md, c_mdt, functions, f_md, f_mdt) From dbdb972ec7e81cca1bcea543e5f46a3fee82dcb0 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 03:14:47 +0530 Subject: [PATCH 006/104] Add scoring to modules Current scheme of scoring includes all members of a module (function/class/methods) as entities and the amount of coverage is defined as total documented entities by total entities. --- bin/coverage_doctest.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 5bf4f79268af..fb0e7006aeda 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -100,6 +100,7 @@ def coverage(module_path, verbose=False): c_has_doctest = [] c_indirect_doctest = [] classes = 0 + c_doctests = 0 f_skipped = [] f_md = [] @@ -107,29 +108,34 @@ def coverage(module_path, verbose=False): f_has_doctest = [] f_indirect_doctest = [] functions = 0 + f_doctests = 0 for member in m_members: # Identify if the member (class/def) a part of this module obj = getattr(m, member) obj_mod = inspect.getmodule(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, simply process it if inspect.isfunction(obj) or inspect.ismethod(obj): - functions = functions + 1 # Various scenarios if member.startswith('_'): f_skipped.append(member) - elif not obj.__doc__: f_md.append(member) - elif not '>>>' in obj.__doc__: f_mdt.append(member) + else: + if not obj.__doc__: f_md.append(member) + elif not '>>>' in obj.__doc__: f_mdt.append(member) + else: f_doctests = f_doctests + 1 + functions = functions + 1 # If it's a class, look at it's methods too elif inspect.isclass(obj): - #print 'Class: '+member classes = classes + 1 # Process the class first if not obj.__doc__: c_md.append(member) elif not '>>>' in obj.__doc__: c_mdt.append(member) + else: c_doctests = c_doctests + 1 # Iterate through it's members for class_m in dir(obj): @@ -152,12 +158,23 @@ def coverage(module_path, verbose=False): # Check function for various categories full_name = member + '.' + class_m if class_m.startswith('_'): f_skipped.append(full_name) - elif not class_m_obj.__doc__: f_md.append(full_name) - elif not '>>>' in class_m_obj.__doc__: f_mdt.append(full_name) - + else: + if not class_m_obj.__doc__: f_md.append(full_name) + elif not '>>>' in class_m_obj.__doc__: f_mdt.append(full_name) + else: f_doctests = f_doctests + 1 + functions = functions + 1 + + total_doctests = c_doctests + f_doctests + total_members = classes + functions print_coverage(classes, c_md, c_mdt, functions, f_md, f_mdt) - return 0, 0 + if total_members: score = 100 * float(total_doctests) / (total_members) + else: score = 0 + + score = int(score) + print '-'*70 + print "SCORE %s: %s%% (%s of %s)" % (module_path, score, total_doctests, total_members) + return total_doctests, total_members def go(file, verbose=False, exact=True): From 8450801d6ee4fd2f78d7c4b4c57c81321d091526 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 04:20:54 +0530 Subject: [PATCH 007/104] Create helper function to print out headers Current underlining was erratic. Changed the underline to the size of text it was underlining. Also created a helper function to make code cleaner. --- bin/coverage_doctest.py | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index fb0e7006aeda..1824eed3a260 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -24,48 +24,37 @@ import inspect from optparse import OptionParser -def print_coverage(c, c_md, c_mdt, f, f_md, f_mdt): - # Print routines (can be deported?) +def print_header(name, underline=None): + print '\n'+name + if underline: print underline*len(name) - print - print 'CLASSES' - print '*'*10 +def print_coverage(c, c_md, c_mdt, f, f_md, f_mdt): + print_header('CLASSES', '*') if not c: - print - print 'No classes found!\n' + print_header('No classes found!') + else: if c_md: - print - print 'Missing docstrings' - print '-'*15 + print_header('Missing docstrings','-') for md in c_md: print md if c_mdt: - print - print 'Missing doctests' - print '-'*15 + print_header('Missing doctests') for md in c_mdt: print md - print - print 'DEFINITIONS' - print '*'*10 + print_header('FUNCTIONS','*') if not f: - print - print 'No functions found!\n' + print_header('No functions found!') else: if f_md: - print - print 'Missing docstrings' - print '-'*15 + print_header('Missing docstrings', '-') for md in f_md: print md if f_mdt: - print - print 'Missing doctests' - print '-'*15 + print_header('Missing doctests', '-') for md in f_mdt: print md @@ -192,6 +181,8 @@ def go(file, verbose=False, exact=True): if not os.path.exists(file): print "File %s does not exist."%file sys.exit(1) + # Handle trailing slashes + if file[:-1] == '/': file = file[:-1] return coverage(string.replace(file,'/','.')[:-3], verbose) if __name__ == "__main__": From 582778f0e4187d248adaf7d951fe46a34dc73a6b Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 04:39:29 +0530 Subject: [PATCH 008/104] Let os.path handle path -> module name conversion Replacing path separators by dot for coversion to module names is scary by using regex based search. os.path.split gives a handy way to recursively stitch parts together. This handles edge cases such as trailing slashes etc. --- bin/coverage_doctest.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 1824eed3a260..8f6440fe726f 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -181,9 +181,18 @@ def go(file, verbose=False, exact=True): if not os.path.exists(file): print "File %s does not exist."%file sys.exit(1) - # Handle trailing slashes - if file[:-1] == '/': file = file[:-1] - return coverage(string.replace(file,'/','.')[:-3], verbose) + + # Remove the file extension + file, ign = os.path.splitext(file) + + # Replace separators by . for module path + file_module = "" + h, t = os.path.split(file) + while h or t: + if t: file_module = t + '.' + file_module + h, t = os.path.split(h) + + return coverage(file_module[:-1], verbose) if __name__ == "__main__": @@ -203,6 +212,7 @@ def go(file, verbose=False, exact=True): parser.print_help() else: for file in args: + file = os.path.normpath(file) doctests, num_functions = go(file, options.verbose) if num_functions == 0: score = 100 From 28df1d419c63ed8264947fb5314ace6ca994f906 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 05:03:27 +0530 Subject: [PATCH 009/104] Add verbose mode Verbose mode is enabled, and the default output is non-verbose. --- bin/coverage_doctest.py | 85 +++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 8f6440fe726f..c50502b474bd 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -25,38 +25,50 @@ from optparse import OptionParser -def print_header(name, underline=None): - print '\n'+name +def print_header(name, underline=None, overline=None): + print + print name if underline: print underline*len(name) -def print_coverage(c, c_md, c_mdt, f, f_md, f_mdt): +def print_coverage(module_path, c, c_md, c_mdt, f, f_md, f_mdt, score, total_doctests, total_members, verbose=False): - print_header('CLASSES', '*') - if not c: - print_header('No classes found!') - else: - 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 - - 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 verbose: + print '\n'+'-'*70 + + print "%s: %s%% (%s of %s)" % (module_path, score, total_doctests, total_members) + + if verbose: + print '-'*70 + + + if verbose: + print_header('CLASSES', '*') + if not c: + print_header('No classes found!') + + else: + 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 + + 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 def coverage(module_path, verbose=False): @@ -77,11 +89,6 @@ def coverage(module_path, verbose=False): # Get the list of members (currently everything possible) m_members = dir(m) - print - print '='*70 - print module_path - print '='*70 - c_skipped = [] c_md = [] @@ -155,14 +162,13 @@ def coverage(module_path, verbose=False): total_doctests = c_doctests + f_doctests total_members = classes + functions - print_coverage(classes, c_md, c_mdt, functions, f_md, f_mdt) - if total_members: score = 100 * float(total_doctests) / (total_members) else: score = 0 - score = int(score) - print '-'*70 - print "SCORE %s: %s%% (%s of %s)" % (module_path, score, total_doctests, total_members) + + print_coverage(module_path, classes, c_md, c_mdt, functions, f_md, f_mdt, score, total_doctests, total_members, verbose) + + return total_doctests, total_members def go(file, verbose=False, exact=True): @@ -213,6 +219,9 @@ def go(file, verbose=False, exact=True): else: for file in args: file = os.path.normpath(file) + print 'DOCTEST for %s' % (file) + print '='*70 + print doctests, num_functions = go(file, options.verbose) if num_functions == 0: score = 100 From 25d70888802a3fc1f9973207cc21e2c15aefe3df Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 05:09:42 +0530 Subject: [PATCH 010/104] Show original exception if module fails to load --- bin/coverage_doctest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index c50502b474bd..0abab9b5605c 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -81,9 +81,9 @@ def coverage(module_path, verbose=False): try: __import__(module_path) m = sys.modules[module_path] - except: + except Exception, a: # Most likely cause, absence of __init__ - print module_path + ' could not be loaded!' + print module_path + ' could not be loaded due to, \"' + a.args[0] + '\"' return 0, 0 # Get the list of members (currently everything possible) From dfd5436250d2d1df1f89a68a4e3b2efbc7f1704b Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 05:32:53 +0530 Subject: [PATCH 011/104] Add indirect doctests * Indirect doctest support added * Printing (verbose) includes doctest now * Fixed minor issues with has_doctest list, though it is not being used currently --- bin/coverage_doctest.py | 45 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 0abab9b5605c..4b9b58d4450a 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -30,7 +30,7 @@ def print_header(name, underline=None, overline=None): print name if underline: print underline*len(name) -def print_coverage(module_path, c, c_md, c_mdt, f, f_md, f_mdt, score, total_doctests, total_members, verbose=False): +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): if verbose: @@ -56,6 +56,11 @@ def print_coverage(module_path, c, c_md, c_mdt, f, f_md, f_mdt, score, total_doc print_header('Missing doctests','-') for md in c_mdt: print md + if f_idt: + print_header('Indirect doctests', '-') + for md in f_idt: + print md + print_header('FUNCTIONS','*') if not f: @@ -69,6 +74,22 @@ def print_coverage(module_path, c, c_md, c_mdt, f, f_md, f_mdt, score, total_doc 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 + +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 coverage(module_path, verbose=False): @@ -120,9 +141,15 @@ def coverage(module_path, verbose=False): # Various scenarios if member.startswith('_'): f_skipped.append(member) else: + if not obj.__doc__: f_md.append(member) elif not '>>>' in obj.__doc__: f_mdt.append(member) - else: f_doctests = f_doctests + 1 + else: + f_doctests = f_doctests + 1 + f_has_doctest.append(member) + if _is_indirect(member, obj.__doc__): + f_indirect_doctest.append(member) + functions = functions + 1 # If it's a class, look at it's methods too @@ -131,7 +158,11 @@ def coverage(module_path, verbose=False): # Process the class first if not obj.__doc__: c_md.append(member) elif not '>>>' in obj.__doc__: c_mdt.append(member) - else: c_doctests = c_doctests + 1 + else: + c_doctests = c_doctests + 1 + c_has_doctest.append(member) + if _is_indirect(member, obj.__doc__): + c_indirect_doctest.append(member) # Iterate through it's members for class_m in dir(obj): @@ -157,7 +188,11 @@ def coverage(module_path, verbose=False): else: if not class_m_obj.__doc__: f_md.append(full_name) elif not '>>>' in class_m_obj.__doc__: f_mdt.append(full_name) - else: f_doctests = f_doctests + 1 + else: + f_has_doctest.append(full_name) + if _is_indirect(member, class_m_obj.__doc__): + f_indirect_doctest.append(full_name) + f_doctests = f_doctests + 1 functions = functions + 1 total_doctests = c_doctests + f_doctests @@ -166,7 +201,7 @@ def coverage(module_path, verbose=False): else: score = 0 score = int(score) - print_coverage(module_path, classes, c_md, c_mdt, functions, f_md, f_mdt, score, total_doctests, total_members, verbose) + print_coverage(module_path, classes, c_md, c_mdt, c_indirect_doctest, functions, f_md, f_mdt, f_indirect_doctest, score, total_doctests, total_members, verbose) return total_doctests, total_members From 0a8dc2ef774d59a9cbe62a42148a8febd4d6d23d Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 05:40:51 +0530 Subject: [PATCH 012/104] Fix documentation comments --- bin/coverage_doctest.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 4b9b58d4450a..5c3622b6fdce 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -32,6 +32,7 @@ def print_header(name, underline=None, overline=None): 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): + """ Prints details (depending on verbose) of a module """ if verbose: print '\n'+'-'*70 @@ -107,7 +108,7 @@ def coverage(module_path, verbose=False): print module_path + ' could not be loaded due to, \"' + a.args[0] + '\"' return 0, 0 - # Get the list of members (currently everything possible) + # Get the list of members m_members = dir(m) @@ -136,7 +137,7 @@ def coverage(module_path, verbose=False): if not obj_mod or not obj_mod.__name__ == module_path: continue - # If it's a function, simply process it + # If it's a function if inspect.isfunction(obj) or inspect.ismethod(obj): # Various scenarios if member.startswith('_'): f_skipped.append(member) @@ -147,6 +148,7 @@ def coverage(module_path, verbose=False): else: f_doctests = f_doctests + 1 f_has_doctest.append(member) + # indirect doctest if _is_indirect(member, obj.__doc__): f_indirect_doctest.append(member) @@ -161,6 +163,7 @@ def coverage(module_path, verbose=False): else: c_doctests = c_doctests + 1 c_has_doctest.append(member) + # indirect doctest if _is_indirect(member, obj.__doc__): c_indirect_doctest.append(member) @@ -178,6 +181,7 @@ def coverage(module_path, verbose=False): except: continue + # Method not part of our module if not class_m_mod or not class_m_obj or \ not class_m_mod.__name__ == module_path: continue @@ -190,11 +194,13 @@ def coverage(module_path, verbose=False): elif not '>>>' in class_m_obj.__doc__: f_mdt.append(full_name) else: f_has_doctest.append(full_name) + # Indirect doctest if _is_indirect(member, class_m_obj.__doc__): f_indirect_doctest.append(full_name) f_doctests = f_doctests + 1 functions = functions + 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) From fdb3320b26be63e8a8f7b5b8feb1aeda83252e0f Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 08:01:33 +0530 Subject: [PATCH 013/104] Remove bare except and introduce skip members Include a list of skip members to skip functions known to create problems (generally builtins). Also this removes a bare except, which can catch random events. --- bin/coverage_doctest.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 5c3622b6fdce..cda90140f7ee 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -128,7 +128,13 @@ def coverage(module_path, verbose=False): functions = 0 f_doctests = 0 + skip_members = ['__abstractmethods__'] + for member in m_members: + # Check for skipped functions first, they throw nasty errors + # when combined with getattr + if member in skip_members: continue + # Identify if the member (class/def) a part of this module obj = getattr(m, member) obj_mod = inspect.getmodule(obj) @@ -170,16 +176,16 @@ def coverage(module_path, verbose=False): # Iterate through it's members for class_m in dir(obj): + # Check in skip function list + if class_m in skip_members: continue + # Check if the method is a part of this module class_m_mod = None class_m_obj = None # Gutsy hack; need to expand reasons - try: - class_m_obj = getattr(obj, class_m) - class_m_mod = inspect.getmodule(class_m_obj) - except: - continue + class_m_obj = getattr(obj, class_m) + class_m_mod = inspect.getmodule(class_m_obj) # Method not part of our module if not class_m_mod or not class_m_obj or \ From 58b789c6857e08f932cdefa4228fc708b0454a0e Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 17:50:32 +0530 Subject: [PATCH 014/104] Add parameter list to verbose output --- bin/coverage_doctest.py | 54 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index cda90140f7ee..a251dc301e61 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -92,6 +92,49 @@ def _is_indirect(member, doc): 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 """ + + argspec = inspect.getargspec(fobj) + + arg_list = [] + + if argspec.args: + for arg in argspec.args: arg_list.append(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) + + + # Construct the parameter string (enclosed in brackets) + if arg_list: + str_param = name + '(' + for arg in arg_list[:-1]: + str_param = str_param + arg + ', ' + str_param = str_param + arg_list[-1] + ')' + else: + str_param = name + '()' + + return str_param + + + def coverage(module_path, verbose=False): """ Given a module path, builds an index of all classes and functions @@ -149,14 +192,15 @@ def coverage(module_path, verbose=False): if member.startswith('_'): f_skipped.append(member) else: - if not obj.__doc__: f_md.append(member) - elif not '>>>' in obj.__doc__: f_mdt.append(member) + param_name = _get_arg_list(member, obj) + if not obj.__doc__: f_md.append(param_name) + elif not '>>>' in obj.__doc__: f_mdt.append(param_name) else: f_doctests = f_doctests + 1 - f_has_doctest.append(member) + f_has_doctest.append(param_name) # indirect doctest if _is_indirect(member, obj.__doc__): - f_indirect_doctest.append(member) + f_indirect_doctest.append(param_name) functions = functions + 1 @@ -193,7 +237,7 @@ def coverage(module_path, verbose=False): continue # Check function for various categories - full_name = member + '.' + class_m + full_name = _get_arg_list(member + '.' + class_m, class_m_obj) if class_m.startswith('_'): f_skipped.append(full_name) else: if not class_m_obj.__doc__: f_md.append(full_name) From f0db8ba06d5f13cbc2bf95c9a9a75efb3737b743 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 17:51:42 +0530 Subject: [PATCH 015/104] Improve indentation while printing function/class names in verbose mode --- bin/coverage_doctest.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index a251dc301e61..cfe059d1f80d 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -52,15 +52,15 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc if c_md: print_header('Missing docstrings','-') for md in c_md: - print md + print '\t* '+md if c_mdt: print_header('Missing doctests','-') for md in c_mdt: - print md - if f_idt: + print '\t* '+md + if c_idt: print_header('Indirect doctests', '-') - for md in f_idt: - print md + for md in c_idt: + print '\t* '+md print_header('FUNCTIONS','*') @@ -70,15 +70,15 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc if f_md: print_header('Missing docstrings', '-') for md in f_md: - print md + print '\t* '+md if f_mdt: print_header('Missing doctests', '-') for md in f_mdt: - print md + print '\t* '+md if f_idt: print_header('Indirect doctests', '-') for md in f_idt: - print md + print '\t* '+md def _is_indirect(member, doc): From 817267c26f48193e42404c5d0fe610f110c17624 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Tue, 13 Mar 2012 17:52:15 +0530 Subject: [PATCH 016/104] Handle the cases where class might have subclass It happens sometimes (once while I was testing) that a class contained another object that inspect recgnized as a class. We need to handle such cases. In this scenario, we ignore because we are concerned only with top level classes and functions/methods --- bin/coverage_doctest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index cfe059d1f80d..bea4b3914403 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -231,6 +231,11 @@ def coverage(module_path, verbose=False): class_m_obj = getattr(obj, class_m) class_m_mod = inspect.getmodule(class_m_obj) + # Check for a function + if not inspect.isfunction(class_m_obj) and \ + not inspect.ismethod(class_m_obj): + continue + # Method not part of our module if not class_m_mod or not class_m_obj or \ not class_m_mod.__name__ == module_path: From e2e6830b51a5b924a21a7a4f73da3272c94907d4 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Wed, 14 Mar 2012 12:34:57 +0530 Subject: [PATCH 017/104] Function parameters might not always be (small) simple strings Sometimes function parameters might not be simple strings. In that case, convert the parameters readable using str(). Also, in addition -- sometimes the parameters are too large for any use. For example a class being passed, or a large list which inspect converts to a large sting. Hence, truncate above a threshold. --- bin/coverage_doctest.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index bea4b3914403..67fae6f6eaaa 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -97,12 +97,14 @@ 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(arg) + for arg in argspec.args: arg_list.append(str(arg)) arg_list.reverse() @@ -121,15 +123,11 @@ def _get_arg_list(name, fobj): 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) - if arg_list: - str_param = name + '(' - for arg in arg_list[:-1]: - str_param = str_param + arg + ', ' - str_param = str_param + arg_list[-1] + ')' - else: - str_param = name + '()' + str_param = "%s(%s)" % (name, ', '.join(arg_list)) return str_param @@ -315,7 +313,7 @@ def go(file, verbose=False, exact=True): else: for file in args: file = os.path.normpath(file) - print 'DOCTEST for %s' % (file) + print 'DOCTEST COVERAGE for %s' % (file) print '='*70 print doctests, num_functions = go(file, options.verbose) From 0dd0eb09f0e998e60db12ba48746d65c9e52f170 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Wed, 14 Mar 2012 16:37:36 +0530 Subject: [PATCH 018/104] Overhaul function and class processing into functions The model for giving doctest coverage follows the steps: 1. Get the list of members in a module 2. If the member is a function - process 3. If it's a class -> Process class -> Process function members of the class Currently this was being done completely inside the code of coverage definition. I have split the task into, process_function and process_class. Thus the code looks much easier and modular to read and understand. --- bin/coverage_doctest.py | 178 +++++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 65 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 67fae6f6eaaa..5c893e116901 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -131,7 +131,88 @@ def _get_arg_list(name, fobj): return str_param +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 + else: + # Indirect doctest + if _is_indirect(name, obj.__doc__): + add_idt = True + 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_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. """ + + c = False + c_dt = False + # Get the line number of class + try: + line_no = inspect.getsourcelines(obj)[1] + except IOError: + # Raised when source does not exist + # which means the class is not there. + return c_dt, c + + 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) + else: + c_dt = True + c_has_doctest.append(full_name) + # indirect doctest + if _is_indirect(c_name, obj.__doc__): + c_idt.append(full_name) + return c_dt, c def coverage(module_path, verbose=False): @@ -149,15 +230,12 @@ def coverage(module_path, verbose=False): print module_path + ' could not be loaded due to, \"' + a.args[0] + '\"' return 0, 0 - # Get the list of members - m_members = dir(m) - c_skipped = [] c_md = [] c_mdt = [] c_has_doctest = [] - c_indirect_doctest = [] + c_idt = [] classes = 0 c_doctests = 0 @@ -165,13 +243,16 @@ def coverage(module_path, verbose=False): f_md = [] f_mdt = [] f_has_doctest = [] - f_indirect_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 @@ -186,72 +267,39 @@ def coverage(module_path, verbose=False): # If it's a function if inspect.isfunction(obj) or inspect.ismethod(obj): - # Various scenarios - if member.startswith('_'): f_skipped.append(member) - else: - - param_name = _get_arg_list(member, obj) - if not obj.__doc__: f_md.append(param_name) - elif not '>>>' in obj.__doc__: f_mdt.append(param_name) - else: - f_doctests = f_doctests + 1 - f_has_doctest.append(param_name) - # indirect doctest - if _is_indirect(member, obj.__doc__): - f_indirect_doctest.append(param_name) - functions = functions + 1 + 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): - classes = classes + 1 + # Process the class first - if not obj.__doc__: c_md.append(member) - elif not '>>>' in obj.__doc__: c_mdt.append(member) - else: - c_doctests = c_doctests + 1 - c_has_doctest.append(member) - # indirect doctest - if _is_indirect(member, obj.__doc__): - c_indirect_doctest.append(member) + c_dt, c = process_class(member, obj, c_md, c_mdt, c_idt, c_has_doctest) + if c: classes += 1 + if c_dt: c_doctests += 1 # Iterate through it's members - for class_m in dir(obj): - - # Check in skip function list - if class_m in skip_members: continue - - # Check if the method is a part of this module - class_m_mod = None - class_m_obj = None - - # Gutsy hack; need to expand reasons - class_m_obj = getattr(obj, class_m) - class_m_mod = inspect.getmodule(class_m_obj) - - # Check for a function - if not inspect.isfunction(class_m_obj) and \ - not inspect.ismethod(class_m_obj): - continue - - # Method not part of our module - if not class_m_mod or not class_m_obj or \ - not class_m_mod.__name__ == module_path: - continue - - # Check function for various categories - full_name = _get_arg_list(member + '.' + class_m, class_m_obj) - if class_m.startswith('_'): f_skipped.append(full_name) - else: - if not class_m_obj.__doc__: f_md.append(full_name) - elif not '>>>' in class_m_obj.__doc__: f_mdt.append(full_name) - else: - f_has_doctest.append(full_name) - # Indirect doctest - if _is_indirect(member, class_m_obj.__doc__): - f_indirect_doctest.append(full_name) - f_doctests = f_doctests + 1 - functions = functions + 1 + for f_name in dir(obj): + + if f_name in skip_members: 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 @@ -260,7 +308,7 @@ def coverage(module_path, verbose=False): else: score = 0 score = int(score) - print_coverage(module_path, classes, c_md, c_mdt, c_indirect_doctest, functions, f_md, f_mdt, f_indirect_doctest, score, total_doctests, total_members, verbose) + print_coverage(module_path, classes, c_md, c_mdt, c_idt, functions, f_md, f_mdt, f_idt, score, total_doctests, total_members, verbose) return total_doctests, total_members From dce60a37dbbd5debbfe8696ef33576bbec25472f Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Wed, 14 Mar 2012 16:40:07 +0530 Subject: [PATCH 019/104] Add usage to option parser --- bin/coverage_doctest.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 5c893e116901..38d10af78b76 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -350,7 +350,13 @@ def go(file, verbose=False, exact=True): if os.path.isdir(sympy_dir): sys.path.insert(0, sympy_top) - parser = OptionParser() + usage = "usage: ./bin/doctest_coverage.py sympy/core" + + parser = OptionParser( + description = __doc__, + usage = usage, + ) + parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False) From efe179ff84d3dbd7901762b7bb7c7a8552ae7bc5 Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Wed, 14 Mar 2012 09:13:28 -0700 Subject: [PATCH 020/104] MatrixBase constructors always make new objects --- sympy/matrices/matrices.py | 2 -- sympy/matrices/tests/test_matrices.py | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sympy/matrices/matrices.py b/sympy/matrices/matrices.py index 37add1cf18dc..99c5ea1d11b5 100644 --- a/sympy/matrices/matrices.py +++ b/sympy/matrices/matrices.py @@ -3241,8 +3241,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 f509c182b4b5..504062617300 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]] From 72107a5bbe5d4e2387d3fbf7dec21926dc2a2454 Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Wed, 14 Mar 2012 09:22:05 -0700 Subject: [PATCH 021/104] removed unnecessary self from _handle_creation_inputs --- sympy/matrices/matrices.py | 86 +++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/sympy/matrices/matrices.py b/sympy/matrices/matrices.py index 99c5ea1d11b5..16951f9fef16 100644 --- a/sympy/matrices/matrices.py +++ b/sympy/matrices/matrices.py @@ -87,73 +87,73 @@ def _handle_creation_inputs(cls, *args, **kwargs): if len(args) == 1 and isinstance(args[0], MatrixBase): return args[0].rows, args[0].cols, args[0].mat - self = object.__new__(cls) 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))) 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) 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])) 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): """ From 6428560e3ec476a00bd7d3d6807ab58b473fc018 Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Wed, 14 Mar 2012 09:31:22 -0700 Subject: [PATCH 022/104] added __array__ method to MatExpr Allows for statements like Matrix(MatrixSymbol('X', 2,2)) --- sympy/matrices/expressions/matexpr.py | 8 ++++++++ sympy/matrices/expressions/tests/test_matrix_exprs.py | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sympy/matrices/expressions/matexpr.py b/sympy/matrices/expressions/matexpr.py index 462f4b9a6a17..583628815adf 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 diff --git a/sympy/matrices/expressions/tests/test_matrix_exprs.py b/sympy/matrices/expressions/tests/test_matrix_exprs.py index 342e6a22e619..b369049dd572 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_converstion(): + 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]]) From c7ab56be3e15502280167077f9fa01ff8fcc0e1f Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Wed, 14 Mar 2012 09:38:32 -0700 Subject: [PATCH 023/104] Matrix(MatExpr) works without numpy Also added inline documentation for matrix creation cases --- sympy/matrices/matrices.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sympy/matrices/matrices.py b/sympy/matrices/matrices.py index 16951f9fef16..fdbf7624a928 100644 --- a/sympy/matrices/matrices.py +++ b/sympy/matrices/matrices.py @@ -84,9 +84,15 @@ 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 + # 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] rows = int(args[0]) @@ -95,6 +101,8 @@ def _handle_creation_inputs(cls, *args, **kwargs): 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]): rows = args[0] cols = args[1] @@ -102,6 +110,8 @@ def _handle_creation_inputs(cls, *args, **kwargs): if len(mat) != rows*cols: raise ValueError('List length should be equal to rows*columns') mat = map(lambda i: sympify(i), mat) + + # Matrix(numpy.ones((2,2))) elif len(args) == 1: in_mat = args[0] if hasattr(in_mat, "__array__"): #pragma: no cover @@ -146,6 +156,8 @@ def _handle_creation_inputs(cls, *args, **kwargs): args) for i in xrange(cols): mat.append(sympify(in_mat[j][i])) + + # Matrix() elif len(args) == 0: # Empty Matrix rows = cols = 0 From cc4c69225a24c83f013a80e0746c2b65bf1502dd Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Wed, 14 Mar 2012 09:43:31 -0700 Subject: [PATCH 024/104] removed "for internal use" in matrixify --- sympy/matrices/expressions/matexpr.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sympy/matrices/expressions/matexpr.py b/sympy/matrices/expressions/matexpr.py index 583628815adf..93a48b122ffb 100644 --- a/sympy/matrices/expressions/matexpr.py +++ b/sympy/matrices/expressions/matexpr.py @@ -304,8 +304,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} From 47507c3b80d6711b3c4dfb848035b89b79f5e880 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Wed, 14 Mar 2012 21:40:41 +0545 Subject: [PATCH 025/104] sort args in __print_Boolean --- sympy/printing/pretty/pretty.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/sympy/printing/pretty/pretty.py b/sympy/printing/pretty/pretty.py index fda55646d84c..168a321d8944 100644 --- a/sympy/printing/pretty/pretty.py +++ b/sympy/printing/pretty/pretty.py @@ -132,7 +132,8 @@ def _print_Not(self, e): else: return self._print_Function(e) - def __print_Boolean(self, args, char): + def __print_Boolean(self, e, char): + args = sorted(e.args, key=default_sort_key) arg = args[0] pform = self._print(arg) @@ -151,47 +152,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") 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) From 22710642acc5294cd03e54a8cc2902ad65d075ad Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 16 Mar 2012 13:01:09 +0530 Subject: [PATCH 026/104] Change usage string --- bin/coverage_doctest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 38d10af78b76..86605840c40c 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -350,7 +350,7 @@ def go(file, verbose=False, exact=True): if os.path.isdir(sympy_dir): sys.path.insert(0, sympy_top) - usage = "usage: ./bin/doctest_coverage.py sympy/core" + usage = "usage: ./bin/doctest_coverage.py PATH" parser = OptionParser( description = __doc__, From da3dc877030648718ce369ea0e65dc22c434243c Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 16 Mar 2012 13:40:27 +0530 Subject: [PATCH 027/104] Doctest coverage can be run from anywhere In order to run, the user simply needs to provide correct paths to the module and the executable. Also it is assumed that module and script lie in the same hierarchy. We construct the module name by using os.path.relpath. Also, in the final reporting -- name.of.module instead of path/to/file is used. --- bin/coverage_doctest.py | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 86605840c40c..4df1480ce364 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -131,6 +131,27 @@ def _get_arg_list(name, fobj): 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. @@ -313,12 +334,12 @@ def coverage(module_path, verbose=False): return total_doctests, total_members -def go(file, verbose=False, exact=True): +def go(sympy_top, 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, exact=False) doctests += _doctests num_functions += _num_functions return doctests, num_functions @@ -330,17 +351,8 @@ def go(file, verbose=False, exact=True): print "File %s does not exist."%file sys.exit(1) - # Remove the file extension - file, ign = os.path.splitext(file) - - # Replace separators by . for module path - file_module = "" - h, t = os.path.split(file) - while h or t: - if t: file_module = t + '.' + file_module - h, t = os.path.split(h) + return coverage(get_mod_name(file, sympy_top), verbose) - return coverage(file_module[:-1], verbose) if __name__ == "__main__": @@ -370,7 +382,7 @@ def go(file, verbose=False, exact=True): print 'DOCTEST COVERAGE for %s' % (file) print '='*70 print - doctests, num_functions = go(file, options.verbose) + doctests, num_functions = go(sympy_top, file, options.verbose) if num_functions == 0: score = 100 else: @@ -379,5 +391,5 @@ def go(file, verbose=False, exact=True): print print '='*70 print "TOTAL SCORE for %s: %s%% (%s of %s)" % \ - (file, score, doctests, num_functions) + (get_mod_name(file, sympy_top), score, doctests, num_functions) print From de898e51fd685940d3d46d9e162cce8a962a6777 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 16 Mar 2012 15:22:37 +0530 Subject: [PATCH 028/104] dir(class_obj) also recognizes inherited functions dir() built-in is not suitable for listing the members(functions) of a class. Instead using class_obj.__dict__ removes such members. Unfortunately this reduces the total members detected. --- bin/coverage_doctest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 4df1480ce364..53bfe9c1c332 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -302,7 +302,7 @@ def coverage(module_path, verbose=False): if c_dt: c_doctests += 1 # Iterate through it's members - for f_name in dir(obj): + for f_name in obj.__dict__: if f_name in skip_members: continue From 34c56bcc020eac115f98080c2053af8584b58ae6 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 16 Mar 2012 15:26:36 +0530 Subject: [PATCH 029/104] Fix output formatting and comments --- bin/coverage_doctest.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 53bfe9c1c332..4d13909d0dbf 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -52,15 +52,17 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc if c_md: print_header('Missing docstrings','-') for md in c_md: - print '\t* '+md + print ' * '+md if c_mdt: print_header('Missing doctests','-') for md in c_mdt: - print '\t* '+md + 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 '\t* '+md + print ' * '+md print_header('FUNCTIONS','*') @@ -70,15 +72,15 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc if f_md: print_header('Missing docstrings', '-') for md in f_md: - print '\t* '+md + print ' * '+md if f_mdt: print_header('Missing doctests', '-') for md in f_mdt: - print '\t* '+md + print ' * '+md if f_idt: print_header('Indirect doctests', '-') for md in f_idt: - print '\t* '+md + print ' * '+md def _is_indirect(member, doc): @@ -248,7 +250,7 @@ def coverage(module_path, verbose=False): m = sys.modules[module_path] except Exception, a: # Most likely cause, absence of __init__ - print module_path + ' could not be loaded due to, \"' + a.args[0] + '\"' + print "%s could not be loaded due to %s." % (module_path, repr(a)) return 0, 0 @@ -351,6 +353,7 @@ def go(sympy_top, file, verbose=False, exact=True): print "File %s does not exist."%file sys.exit(1) + # Relpath for constructing the module name return coverage(get_mod_name(file, sympy_top), verbose) From fcab4b6b2b44e23581b8fd80031e123adbdee529 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 16 Mar 2012 15:27:11 +0530 Subject: [PATCH 030/104] Sort members by line-number in verbose output --- bin/coverage_doctest.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 4d13909d0dbf..fe82906fa75a 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -331,6 +331,15 @@ def coverage(module_path, verbose=False): else: score = 0 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) From b923139c58d1ce090c479e1e36ccb52570ad1524 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 16 Mar 2012 15:38:17 +0530 Subject: [PATCH 031/104] Exclude indirect-doctests from coverage Indirect doctests are not part of coverage -- that is they need to be looked into. Hence they should not account for "documented" members. --- bin/coverage_doctest.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index fe82906fa75a..16b136baa181 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -185,11 +185,10 @@ def process_function(name, c_name, b_obj, mod_path, f_sk, f_md, f_mdt, f_idt, f_ add_md = True elif not '>>>' in obj.__doc__: add_mdt = True - else: - # Indirect doctest - if _is_indirect(name, obj.__doc__): + elif _is_indirect(name, obj.__doc__): add_idt = True - f_doctest = True + else: f_doctest = True + function = True if add_md or add_mdt or add_idt: @@ -229,12 +228,11 @@ def process_class(c_name, obj, c_md, c_mdt, c_idt, c_has_doctest): 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) - else: + elif _is_indirect(c_name, obj.__doc__): + c_idt.append(full_name) + else: # indirect doctest c_dt = True c_has_doctest.append(full_name) - # indirect doctest - if _is_indirect(c_name, obj.__doc__): - c_idt.append(full_name) return c_dt, c def coverage(module_path, verbose=False): From 35e11b57fab51b3827631648add8cae1a5358ec7 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 16 Mar 2012 15:39:31 +0530 Subject: [PATCH 032/104] Print module coverage after, not before in verbose mode --- bin/coverage_doctest.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 16b136baa181..26a6bec17a64 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -36,11 +36,12 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc if verbose: print '\n'+'-'*70 + print module_path + print '-'*70 + else: + print "%s: %s%% (%s of %s)" % (module_path, score, total_doctests, total_members) - print "%s: %s%% (%s of %s)" % (module_path, score, total_doctests, total_members) - if verbose: - print '-'*70 if verbose: @@ -82,6 +83,12 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc for md in f_idt: print ' * '+md + if verbose: + print '\n'+'-'*70 + print "SCORE: %s%% (%s of %s)" % (score, total_doctests, total_members) + print '-'*70 + + def _is_indirect(member, doc): """ Given string repr of doc and member checks if the member From 33b3147a644a3be3f1296735a07978dff48a8b40 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 15 Apr 2011 19:38:22 +0545 Subject: [PATCH 033/104] subs cleanup All input to subs is reduced to a sequence of old, new tuples. If the input is a dict or set (unordered containers) the replacements are ordered. A special routine is used for dict; sets are sorted by op count and in case of a tie, by the number of arguments. This reduction to a single container allows all the individual routines for different containers to be removed. In their place is _subs which handles a single old->new substitution and caches results. That routine also handles the case of self == old, allowing that to be factored out of all _eval_subs routines. An _eval_subs routine only needs to be written if more than just trying the replacement on all the args of the object. For example, Integrals need to watch out for the integration variables and not allow certain changes to them. Many objects (especially in secondquant) were essentially duplicating what is now the fallback behavior in _subs (i.e. trying the substitution on all arguments) so they could be (and were) removed. The recursion by _eval_subs into sub-arguments has been removed: when _eval_subs has not made any changes and wants to try the replacement of old -> new on sub-arguments, it should return None and this will be handled by _subs. If no further processing of the expression is needed then an expression should be returned. Summary: call subs with raw old, new pairs: they will be sympified and sorted if necessary. _eval_subs need only deal with the case of self != old and should not do any recursive calls. If the subs cannot be performed on the given object and it should be tried on the sub-arguments of the object, then None should be returned. If further substitution of a subset of an expressions arguments needs to be performed, _subs (not _eval_subs) should be called on them; alternatively, if a subexpression (rather than a subset of an expressions arguments) needs to be processed, then _subs should be called on it, too. There are further developer's notes in the subs docstring. Other changes: o remove special trig subs behavior: tan(x).subs(x,pi/2)->zoo o fix GeometryEntity.subs o eliminate hasattr check in _subs o give Basic _eval_subs routine o remove O(n**2) sorting from subs This is not currently needed. Perhaps if some sort of wild patterns were used to do replacements something other than the current method would be necessary, but nothing fails with the current sorting scheme. o update xreplace docstring --- sympy/concrete/summations.py | 14 +- sympy/core/add.py | 27 +- sympy/core/basic.py | 265 ++++++++++++------ sympy/core/function.py | 18 +- sympy/core/mul.py | 56 ++-- sympy/core/power.py | 3 - sympy/core/relational.py | 5 - sympy/core/sets.py | 13 +- sympy/core/tests/test_basic.py | 2 +- sympy/core/tests/test_expr.py | 19 -- sympy/core/tests/test_subs.py | 56 +++- sympy/functions/elementary/exponential.py | 23 +- sympy/functions/elementary/piecewise.py | 26 +- .../elementary/tests/test_exponential.py | 7 +- .../elementary/tests/test_trigonometric.py | 9 +- sympy/functions/elementary/trigonometric.py | 18 -- sympy/geometry/entity.py | 8 +- sympy/geometry/tests/test_geometry.py | 1 + sympy/integrals/integrals.py | 4 +- sympy/integrals/tests/test_integrals.py | 4 +- sympy/physics/quantum/dagger.py | 4 - sympy/physics/secondquant.py | 36 --- sympy/series/order.py | 10 +- 23 files changed, 332 insertions(+), 296 deletions(-) diff --git a/sympy/concrete/summations.py b/sympy/concrete/summations.py index dbb451a75fee..78c79c7b9816 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/add.py b/sympy/core/add.py index 057ccfc97753..c6fe6624dea2 100644 --- a/sympy/core/add.py +++ b/sympy/core/add.py @@ -533,10 +533,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 +545,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 = (-terms_old).args # (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/basic.py b/sympy/core/basic.py index cd214b743fcc..3fb6c964e30d 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -739,122 +739,195 @@ def as_content_primitive(self, radical=False): def subs(self, *args): """ - 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. The resulting sorted list is + then processed as an iterable container (see previous). 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 + + When unordered iterables are given they are sorted so a canonical result + is obtained, by count_op length and then by number of arguments to break + any ties. All other iterables are 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 + """ + from sympy.core.containers import Dict + + 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 = [i for i in sequence.iteritems()] + elif not iterable(sequence): + raise TypeError("subs accepts an iterable with 1 or 2 arguments") elif len(args) == 2: - old, new = args - return self._subs_old_new(old, new) + sequence = [args] else: - raise TypeError("subs accepts either 1 or 2 arguments") + raise ValueError("subs accepts either 1 or 2 arguments") + + sequence = [(sympify(old), sympify(new)) for old, new in sequence] + if unordered: + sequence.sort(key=lambda x: (x[0].count_ops(), len(x[0].args)), reverse=True) + + 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: + + 1) any arguments are not instances of Basic (e.g. bool, tuple); - >>> from sympy.abc import a, b, c, d, e + 2) some arguments should not be targeted (as in integration + variables); - >>> A = (sqrt(sin(2*x)), a) - >>> B = (sin(2*x), b) - >>> C = (cos(2*x), c) - >>> D = (x, d) - >>> E = (exp(x), e) + 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 = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) + If it is overridden here are some special cases that might arise: - >>> expr._subs_dict([A,B,C,D,E]) - a*c*sin(d*e) + b + 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): + 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 self == old: + return new + if not self.args: + return self - 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): """ @@ -871,20 +944,15 @@ def xreplace(self, rule): Examples -------- - >>> from sympy import symbols, pi + >>> from sympy import symbols, pi, exp >>> x,y, z = symbols('x y z') - >>> (1+x*y).xreplace({x: pi}) + >>> (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 +962,24 @@ 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),) + """ if self in rule: return rule[self] @@ -1335,11 +1421,8 @@ 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 as_numer_denom(self): + return self, S.One def xreplace(self, rule): return rule.get(self, self) diff --git a/sympy/core/function.py b/sympy/core/function.py index a8a3839aa244..8f86e5cb18d7 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): @@ -1136,15 +1132,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..ee05a11cdc31 100644 --- a/sympy/core/mul.py +++ b/sympy/core/mul.py @@ -1158,20 +1158,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,9 +1208,6 @@ def ndiv(a, b): return int(a/b) return 0 - if not old.is_Mul: - return fallback() - # handle the leading coefficient and use it to decide if anything # should even be started; we always know where to find the Rational # so it's a quick test @@ -1233,7 +1222,7 @@ def ndiv(a, b): co_xmul = True if not co_xmul: - return fallback() + return None (c, nc) = breakup(self) (old_c, old_nc) = breakup(old) @@ -1245,16 +1234,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 None if not old_c: cdid = None @@ -1264,7 +1268,7 @@ def ndiv(a, b): c_e = c[b] rat.append(ndiv(c_e, old_e)) if not rat[-1]: - return fallback() + return None cdid = min(rat) if not old_nc: @@ -1346,7 +1350,7 @@ def ndiv(a, b): else: if not ncdid: - return fallback() + return None # although we didn't fail, certain nc terms may have # failed so we rebuild them after attempting a partial diff --git a/sympy/core/power.py b/sympy/core/power.py index a513c2714f4e..ebe9b15e9a86 100644 --- a/sympy/core/power.py +++ b/sympy/core/power.py @@ -241,8 +241,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 +258,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. 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..efef9e0c5711 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -206,16 +206,21 @@ def __contains__(self, other): return result def _eval_subs(self, old, new): - if self == old: - return new new_args = [] + hit = False for arg in self.args: if arg == old: new_args.append(new) + hit = True elif isinstance(arg, Basic): - new_args.append(arg._eval_subs(old, new)) - else: + newa = arg._subs(old, new) + if not hit and newa != arg: + hit = True + new_args.append(newa) + else: # e.g. bool will come here new_args.append(arg) + if not hit: + return self return self.__class__(*new_args) @property diff --git a/sympy/core/tests/test_basic.py b/sympy/core/tests/test_basic.py index bd508465f320..efd50e127007 100644 --- a/sympy/core/tests/test_basic.py +++ b/sympy/core/tests/test_basic.py @@ -56,7 +56,7 @@ 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(b1, b2, b3)") def test_atoms(): assert b21.atoms() == set() diff --git a/sympy/core/tests/test_expr.py b/sympy/core/tests/test_expr.py index 6168c4e19668..8e3303845aee 100644 --- a/sympy/core/tests/test_expr.py +++ b/sympy/core/tests/test_expr.py @@ -497,25 +497,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. diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index a642fa1468a3..daa45ef758d2 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -32,13 +32,14 @@ def test_trigonometric(): 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') @@ -83,8 +84,8 @@ def test_dict(): 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 + 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 @@ -100,8 +101,8 @@ 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) @@ -326,6 +327,10 @@ 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 + def test_subs_issue910(): assert (I*Symbol("a")).subs(1, 2) == I*Symbol("a") @@ -365,8 +370,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') diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py index 3a2c1e4b2777..edc258afbaff 100644 --- a/sympy/functions/elementary/exponential.py +++ b/sympy/functions/elementary/exponential.py @@ -303,16 +303,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 +318,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 diff --git a/sympy/functions/elementary/piecewise.py b/sympy/functions/elementary/piecewise.py index 15eaacce2265..a1afe4d23412 100644 --- a/sympy/functions/elementary/piecewise.py +++ b/sympy/functions/elementary/piecewise.py @@ -254,19 +254,27 @@ def _eval_derivative(self, s): return Piecewise(*[(diff(e, s), c) for e, c in self.args]) def _eval_subs(self, old, new): - if self == old: - return new - new_args = [] - for e, c in self.args: + """ + 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. + """ + args = list(self.args) + for i, (e, c) in enumerate(args): + e = e._subs(old, new) + if isinstance(c, bool): - new_args.append((e._eval_subs(old, new), c)) + pass elif isinstance(c, Set): # What do we do if there are more than one symbolic # variable. Which do we put pass to Set.contains? - new_args.append((e._eval_subs(old, new), c.contains(new))) - else: - new_args.append((e._eval_subs(old, new), c._eval_subs(old, new))) - return Piecewise( *new_args ) + c = c.contains(new) + elif isinstance(c, Basic): + c = c._subs(old, new) + + args[i] = e, c + + return Piecewise(*args) def _eval_nseries(self, x, n, logx): args = map(lambda ec: (ec.expr._eval_nseries(x, n, logx), ec.cond), \ diff --git a/sympy/functions/elementary/tests/test_exponential.py b/sympy/functions/elementary/tests/test_exponential.py index 63ac25934379..33bd06a4b0b7 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) == x**3 + assert e.subs(x**2, 5) == x**3 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) 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..784c94d8cf84 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_()) diff --git a/sympy/geometry/entity.py b/sympy/geometry/entity.py index 07b94fb887c9..88959f95403d 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,9 @@ 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) diff --git a/sympy/geometry/tests/test_geometry.py b/sympy/geometry/tests/test_geometry.py index 8a83d16cf9e1..e8abb909317c 100644 --- a/sympy/geometry/tests/test_geometry.py +++ b/sympy/geometry/tests/test_geometry.py @@ -844,6 +844,7 @@ 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) def test_encloses(): # square with a dimpled left side diff --git a/sympy/integrals/integrals.py b/sympy/integrals/integrals.py index 86f4ef9d7a61..a7fb672bf811 100644 --- a/sympy/integrals/integrals.py +++ b/sympy/integrals/integrals.py @@ -868,8 +868,6 @@ 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) @@ -886,7 +884,7 @@ def _eval_subs(self, old, new): xab = limits[i] 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/tests/test_integrals.py b/sympy/integrals/tests/test_integrals.py index ea4b9cbfd9ba..4bb297ea4e60 100644 --- a/sympy/integrals/tests/test_integrals.py +++ b/sympy/integrals/tests/test_integrals.py @@ -422,8 +422,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/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/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 From 7611d52b150c7f5ac932b32abcddd596db7b55ff Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sat, 5 Nov 2011 23:28:37 +0545 Subject: [PATCH 034/104] keep only Expr.as_numer_denom() stub It doesn't make sense for Basic to have a numerator and denominator. AtomicExpr derives from Expr, so it inherits Expr's as_numer_denom stub. --- sympy/core/basic.py | 3 --- sympy/core/expr.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index 3fb6c964e30d..dad826abc481 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -1421,9 +1421,6 @@ def matches(self, expr, repl_dict={}): if self == expr: return repl_dict - def as_numer_denom(self): - return self, S.One - def xreplace(self, rule): return rule.get(self, self) diff --git a/sympy/core/expr.py b/sympy/core/expr.py index b148c4438cf7..7298882fa650 100644 --- a/sympy/core/expr.py +++ b/sympy/core/expr.py @@ -2670,9 +2670,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 From a64a16c1256afe03585927b51eee9e7101f85596 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Mon, 19 Dec 2011 14:40:56 +0545 Subject: [PATCH 035/104] 2552: give Mul._eval_subs a chance to handle Muls in denominator --- sympy/core/mul.py | 19 +++++++++++++++---- sympy/core/tests/test_subs.py | 16 ++++++++++------ sympy/solvers/tests/test_ode.py | 5 +++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/sympy/core/mul.py b/sympy/core/mul.py index ee05a11cdc31..c364a648ad4e 100644 --- a/sympy/core/mul.py +++ b/sympy/core/mul.py @@ -1208,6 +1208,17 @@ def ndiv(a, b): return int(a/b) return 0 + # 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: + was = self + self = n/d._subs(old, new) + if self != was: + rv = self + # 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 # so it's a quick test @@ -1222,7 +1233,7 @@ def ndiv(a, b): co_xmul = True if not co_xmul: - return None + return rv (c, nc) = breakup(self) (old_c, old_nc) = breakup(old) @@ -1258,7 +1269,7 @@ def ndiv(a, b): any(sign(c[b]) != sign(old_c[b]) for b in old_c)): ok = False if not ok: - return None + return rv if not old_c: cdid = None @@ -1268,7 +1279,7 @@ def ndiv(a, b): c_e = c[b] rat.append(ndiv(c_e, old_e)) if not rat[-1]: - return None + return rv cdid = min(rat) if not old_nc: @@ -1350,7 +1361,7 @@ def ndiv(a, b): else: if not ncdid: - return None + 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/tests/test_subs.py b/sympy/core/tests/test_subs.py index daa45ef758d2..c9a554261fc4 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -260,12 +260,6 @@ def test_subs_basic_funcs(): 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 - def test_subs_wild(): R, S, T, U = symbols('R, S, T, U', cls=Wild) @@ -416,3 +410,13 @@ 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) + assert (a/(b*c)).subs(b*c, K) == a/K + assert (a/(b**2*c**3)).subs(b*c, K) == a/(c*K**2) diff --git a/sympy/solvers/tests/test_ode.py b/sympy/solvers/tests/test_ode.py index 941bf567c1bf..1d83ba78cf77 100644 --- a/sympy/solvers/tests/test_ode.py +++ b/sympy/solvers/tests/test_ode.py @@ -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] From b086641a9037c4ab521bc77347826d2ca64e54f5 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Tue, 10 Jan 2012 15:36:14 +0545 Subject: [PATCH 036/104] matexpr comment added to _eval_subs --- sympy/matrices/expressions/matexpr.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sympy/matrices/expressions/matexpr.py b/sympy/matrices/expressions/matexpr.py index 462f4b9a6a17..74962b0b1e3c 100644 --- a/sympy/matrices/expressions/matexpr.py +++ b/sympy/matrices/expressions/matexpr.py @@ -220,14 +220,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 From 8b7585cdfc5cf05eaa414bb00a8069282c314172 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Tue, 10 Jan 2012 15:36:39 +0545 Subject: [PATCH 037/104] 2976: check for _subs attribute --- sympy/core/basic.py | 2 ++ sympy/core/sets.py | 18 ------------------ sympy/core/tests/test_subs.py | 3 +++ 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index dad826abc481..34d06efc263f 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -903,6 +903,8 @@ def fallback(self, old, new): hit = False args = list(self.args) for i, arg in enumerate(args): + if not hasattr(arg, '_subs'): + continue arg = arg._subs(old, new, **hints) if arg is not args[i]: hit = True diff --git a/sympy/core/sets.py b/sympy/core/sets.py index efef9e0c5711..a083bb922ebe 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -205,24 +205,6 @@ def __contains__(self, other): raise TypeError('contains did not evaluate to a bool: %r' % result) return result - def _eval_subs(self, old, new): - new_args = [] - hit = False - for arg in self.args: - if arg == old: - new_args.append(new) - hit = True - elif isinstance(arg, Basic): - newa = arg._subs(old, new) - if not hit and newa != arg: - hit = True - new_args.append(newa) - else: # e.g. bool will come here - new_args.append(arg) - if not hit: - return self - return self.__class__(*new_args) - @property def is_number(self): return False diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index c9a554261fc4..cda0d5b90de0 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -420,3 +420,6 @@ def test_issue_2552(): a,b,c,K = symbols('a b c K', commutative=True) assert (a/(b*c)).subs(b*c, K) == a/K assert (a/(b**2*c**3)).subs(b*c, K) == a/(c*K**2) + +def test_issue_2976(): + assert Tuple(1, True).subs(1, 2) == Tuple(2, True) From 291c08d211e3c4de38d9dcc665ca4502c5e54155 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Tue, 10 Jan 2012 19:16:18 +0545 Subject: [PATCH 038/104] complete coverage of subs cleanup work --- sympy/core/basic.py | 5 ++++- sympy/core/tests/test_basic.py | 2 +- sympy/core/tests/test_subs.py | 12 ++++++++---- sympy/geometry/entity.py | 3 +++ sympy/geometry/tests/test_geometry.py | 4 ++++ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index 34d06efc263f..8402b4018202 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -807,7 +807,10 @@ def subs(self, *args): unordered = True sequence = [i for i in sequence.iteritems()] elif not iterable(sequence): - raise TypeError("subs accepts an iterable with 1 or 2 arguments") + 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: sequence = [args] else: diff --git a/sympy/core/tests/test_basic.py b/sympy/core/tests/test_basic.py index efd50e127007..c12c7d06836f 100644 --- a/sympy/core/tests/test_basic.py +++ b/sympy/core/tests/test_basic.py @@ -55,7 +55,7 @@ def test_subs(): assert b21.subs({b1: b2, b2: b1}) == Basic(b2, b2) - raises(TypeError, "b21.subs('bad arg')") + raises(ValueError, "b21.subs('bad arg')") raises(ValueError, "b21.subs(b1, b2, b3)") def test_atoms(): diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index cda0d5b90de0..a106a25eaa85 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -76,7 +76,7 @@ def test_subbug2(): 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') @@ -86,6 +86,9 @@ def test_dict(): 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 @@ -308,9 +311,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) diff --git a/sympy/geometry/entity.py b/sympy/geometry/entity.py index 88959f95403d..eb5871824e8f 100644 --- a/sympy/geometry/entity.py +++ b/sympy/geometry/entity.py @@ -310,3 +310,6 @@ def _eval_subs(self, old, 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 e8abb909317c..5ca5e8f7fa77 100644 --- a/sympy/geometry/tests/test_geometry.py +++ b/sympy/geometry/tests/test_geometry.py @@ -845,6 +845,10 @@ def test_subs(): 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 From 849af4c2f0200d4a55ac3df50cf0448ff959d0fc Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Wed, 11 Jan 2012 16:52:36 +0545 Subject: [PATCH 039/104] let equivalent new/old subs pass; use better test for self==old --- sympy/core/basic.py | 70 +++++++++++++++++++++++++++++++---- sympy/core/tests/test_subs.py | 14 ++++++- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index 8402b4018202..48199c2c2ea8 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 @@ -822,6 +826,8 @@ def subs(self, *args): rv = self for old, new in sequence: + if _aresame(old, new): + continue rv = rv._subs(old, new) if not isinstance(rv, Basic): break @@ -916,7 +922,7 @@ def fallback(self, old, new): return self.func(*args) return self - if self == old: + if _aresame(self, old): return new if not self.args: return self @@ -1444,3 +1450,51 @@ 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 diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index a106a25eaa85..11a506e8bcb6 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -1,6 +1,7 @@ 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) + cse, cot, tan, S, Tuple, Basic) +from sympy.core.basic import _aresame from sympy.utilities.pytest import XFAIL def test_subs(): @@ -427,3 +428,14 @@ def test_issue_2552(): 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)) From 527de067a1db5a962fa25e86972ad76c60d2dbeb Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sun, 5 Feb 2012 13:17:50 +0545 Subject: [PATCH 040/104] 1581: prefer str->Symbol in subs --- sympy/core/basic.py | 9 ++++++++- sympy/core/tests/test_subs.py | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index 48199c2c2ea8..47ae2fdea801 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -820,7 +820,14 @@ def subs(self, *args): else: raise ValueError("subs accepts either 1 or 2 arguments") - sequence = [(sympify(old), sympify(new)) for old, new in sequence] + 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 unordered: sequence.sort(key=lambda x: (x[0].count_ops(), len(x[0].args)), reverse=True) diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 11a506e8bcb6..1f7e02e1d906 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -439,3 +439,7 @@ def test_issue_2980(): 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 From b5e08ca9f5d6eb5536c42d051e30bc62691a0d3f Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sun, 5 Feb 2012 13:38:03 +0545 Subject: [PATCH 041/104] update exp subs tests to pass The input expression is unchanged if no substitutions take place. --- sympy/functions/elementary/tests/test_exponential.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sympy/functions/elementary/tests/test_exponential.py b/sympy/functions/elementary/tests/test_exponential.py index 33bd06a4b0b7..e182bcdf8f03 100644 --- a/sympy/functions/elementary/tests/test_exponential.py +++ b/sympy/functions/elementary/tests/test_exponential.py @@ -87,8 +87,8 @@ def test_exp_infinity(): def test_exp_subs(): x, y = symbols('x,y') e = (exp(3*log(x), evaluate=False)) - assert e.subs(x**3, y**3) == x**3 - assert e.subs(x**2, 5) == x**3 + 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) From 68dc0e64c1e3f7b3331f2c5b426f6b59c10deed0 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sun, 5 Feb 2012 16:20:12 +0545 Subject: [PATCH 042/104] 2749: use items() to get data from dict and Dict --- sympy/core/basic.py | 2 +- sympy/core/tests/test_subs.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index 47ae2fdea801..f81945a5fc96 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -809,7 +809,7 @@ def subs(self, *args): unordered = True elif isinstance(sequence, (Dict, dict)): unordered = True - sequence = [i for i in sequence.iteritems()] + sequence = sequence.items() elif not iterable(sequence): from sympy.utilities.misc import filldedent raise ValueError(filldedent(""" diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 1f7e02e1d906..233e61b5e90b 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -1,6 +1,6 @@ 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, Basic) + cse, cot, tan, S, Tuple, Basic, Dict) from sympy.core.basic import _aresame from sympy.utilities.pytest import XFAIL @@ -93,6 +93,7 @@ def test_dict_set(): 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') From 7580b6b4201e2f569c97f0bef8a5ede8e306949d Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sun, 19 Feb 2012 18:07:07 +0545 Subject: [PATCH 043/104] default_sort_key breaks ties in subs sort --- sympy/core/basic.py | 14 +++++++++++--- sympy/core/tests/test_subs.py | 7 +++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index f81945a5fc96..df96fd8e6908 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -753,8 +753,9 @@ def subs(self, *args): 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. The resulting sorted list is - then processed as an iterable container (see previous). + 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). Examples ======== @@ -801,6 +802,7 @@ def subs(self, *args): """ from sympy.core.containers import Dict + from sympy.utilities import default_sort_key, sift unordered = False if len(args) == 1: @@ -829,7 +831,13 @@ def subs(self, *args): so = C.Symbol(o) sequence[i] = (so, sn) if unordered: - sequence.sort(key=lambda x: (x[0].count_ops(), len(x[0].args)), reverse=True) + sequence = dict(sequence) + d = sift(sequence.iteritems(), lambda i: (i[0].count_ops(), len(i[0].args))) + 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 rv = self for old, new in sequence: diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 233e61b5e90b..77d7e0298039 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -113,6 +113,13 @@ def test_dict_ambigous(): # see #467 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 + def test_deriv_sub_bug3(): x = Symbol("x") y = Symbol("y") From 9976da486fe23c203b0ab311b472502771ef9323 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 23 Feb 2012 00:15:01 +0545 Subject: [PATCH 044/104] raise error if an unordered sequence appears ambiguous The ambiguous() function is not guaranteed to catch all ambiguous substitution sequences nor does it guarantee that for a given expr the apparently ambiguous sequence wouldn't have been ambiguous. It's just a safeguard that will raise an error (unless suppressed with the keyword, `warn`) if it appears that the order of substitutions will matter. Even if the error is not raised, unordered sequences will still be ordered before substitution. --- sympy/core/basic.py | 83 ++++++++++++++++++++++++++++++++--- sympy/core/tests/test_subs.py | 15 ++++--- 2 files changed, 86 insertions(+), 12 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index df96fd8e6908..9080739f5e6c 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -741,7 +741,7 @@ def as_content_primitive(self, radical=False): """ return S.One, self - def subs(self, *args): + def subs(self, *args, **kwargs): """ Substitutes old for new in an expression after sympifying args. @@ -784,7 +784,8 @@ def subs(self, *args): When unordered iterables are given they are sorted so a canonical result is obtained, by count_op length and then by number of arguments to break - any ties. All other iterables are unsorted. + any ties. All other iterables are unsorted. A ValueError will be raised + if the substitution appears ambiguous unless flag ``warn=False``. >>> from sympy import sqrt, sin, cos, exp >>> from sympy.abc import a, b, c, d, e @@ -797,12 +798,15 @@ def subs(self, *args): >>> expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) - >>> expr.subs(dict([A,B,C,D,E])) + >>> expr.subs(dict([A,B,C,D,E]), warn=False) a*c*sin(d*e) + b """ + 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: @@ -830,8 +834,38 @@ def subs(self, *args): 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) + def ambiguous(rules_dict): + """Return True if it appears that there could be ambiguity + in the substitution as a result of overlapping keys and + values.""" + val = set() + key = set() + for k, v in rules_dict.iteritems(): + kfree = _atomic(k) + # if x and cos(x) are keys, the result depends on + # which one gets replaced first + if any(ki in key for ki in kfree): + return True + # if any other substitution could introduce the key + # then the order will matter + if any(ki in val for ki in kfree): + return True + # if this substitution will introduce anything that + # has already been targeted before then its order matters + vfree = _atomic(v) + if any(vi in key for vi in vfree): + return True + val.update(vfree) + key.update(kfree) + if kwargs.pop('warn', True) and ambiguous(sequence): + raise ValueError('ambiguous unordered sequence %s; send items in a list' % sequence) d = sift(sequence.iteritems(), lambda i: (i[0].count_ops(), len(i[0].args))) newseq = [] for k in sorted(d.keys(), reverse=True): @@ -841,8 +875,6 @@ def subs(self, *args): rv = self for old, new in sequence: - if _aresame(old, new): - continue rv = rv._subs(old, new) if not isinstance(rv, Basic): break @@ -1513,3 +1545,44 @@ def _aresame(a, b): 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/tests/test_subs.py b/sympy/core/tests/test_subs.py index 77d7e0298039..2252e409845a 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -2,7 +2,7 @@ abc, Integer, Eq, symbols, Add, I, Float, log, Rational, Lambda, atan2, cse, cot, tan, S, Tuple, Basic, Dict) from sympy.core.basic import _aresame -from sympy.utilities.pytest import XFAIL +from sympy.utilities.pytest import XFAIL, raises def test_subs(): n3 = Rational(3) @@ -106,8 +106,8 @@ def test_dict_ambigous(): # see #467 df= {x:y, exp(x): y} dg= {z:y, exp(z): y} - assert f.subs(df) == y**2 - assert g.subs(dg) == y**2 + assert f.subs(df, warn=False) == y**2 + assert g.subs(dg, warn=False) == y**2 # and this is how order can affect the result assert f .subs(x,y) .subs(exp(x),y) == y*exp(y) @@ -118,7 +118,8 @@ def test_dict_ambigous(): # see #467 # 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 + assert e.subs({x: y, y: 2}, warn=False) == 5 + raises(ValueError, 'x.subs({x: x**2 + 1, y: x + y})') def test_deriv_sub_bug3(): x = Symbol("x") @@ -383,8 +384,8 @@ def test_subs_dict(): 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(l, warn=False) == \ + (sin(x)).subs(dict(l), warn=False) == 2 assert sin(x).subs(reversed(l)) == sin(1) expr = sin(2*x) + sqrt(sin(2*x))*cos(2*x)*sin(exp(x)*x) @@ -395,7 +396,7 @@ def test_subs_dict(): (exp(x),e), (x, d), ]) - assert expr.subs(reps) == c + a*b*sin(d*e) + assert expr.subs(reps, warn=False) == c + a*b*sin(d*e) l = [(x, 3), (y, x**2)] assert (x+y).subs(l) == 3 + x**2 From eea1537f6ed9f87148ace9f8604182a2a5be57a7 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sat, 25 Feb 2012 09:09:02 +0545 Subject: [PATCH 045/104] watch for negated Numbers` --- sympy/core/basic.py | 6 ++---- sympy/core/numbers.py | 5 +++++ sympy/core/tests/test_subs.py | 9 ++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index 9080739f5e6c..d24518c31805 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -928,7 +928,7 @@ def _subs(self, old, new, **hints): that should be attempted (as in Piecewise where the condition may be updated without doing a replacement). - If it is overridden here are some special cases that might arise: + 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 @@ -959,7 +959,7 @@ def fallback(self, old, new): hit = False args = list(self.args) for i, arg in enumerate(args): - if not hasattr(arg, '_subs'): + if not hasattr(arg, '_eval_subs'): continue arg = arg._subs(old, new, **hints) if arg is not args[i]: @@ -971,8 +971,6 @@ def fallback(self, old, new): if _aresame(self, old): return new - if not self.args: - return self rv = self._eval_subs(old, new) if rv is None: diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py index b1ef7bff010f..f2adde8c06d2 100644 --- a/sympy/core/numbers.py +++ b/sympy/core/numbers.py @@ -245,6 +245,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' diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 2252e409845a..821c4d191cc8 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -1,6 +1,6 @@ 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, Basic, Dict) + cse, cot, tan, S, Tuple, Basic, Dict, oo) from sympy.core.basic import _aresame from sympy.utilities.pytest import XFAIL, raises @@ -452,3 +452,10 @@ def test_issue_2980(): 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 From 40f4799e3e507f08aebcd3ca873d756eb86271e6 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sat, 25 Feb 2012 10:32:35 +0545 Subject: [PATCH 046/104] watch for Piecewise UndefinedFunction-like args --- sympy/core/tests/test_subs.py | 9 ++++++++- sympy/functions/elementary/piecewise.py | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 821c4d191cc8..5db6ed01a9a1 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -1,6 +1,6 @@ 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, Basic, Dict, oo) + cse, cot, tan, S, Tuple, Basic, Dict, Piecewise, oo) from sympy.core.basic import _aresame from sympy.utilities.pytest import XFAIL, raises @@ -459,3 +459,10 @@ def test_issue_3059(): 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_Piecewise_subs(): + from sympy.abc import x, y + g = Function('g') + h = Function('h') + p = Piecewise((g, x < -1), (g(x), x <= 1)) + assert p.subs(g, h) == Piecewise((h, x < -1), (h(x), x <= 1)) diff --git a/sympy/functions/elementary/piecewise.py b/sympy/functions/elementary/piecewise.py index a1afe4d23412..18ab05ae9ed7 100644 --- a/sympy/functions/elementary/piecewise.py +++ b/sympy/functions/elementary/piecewise.py @@ -261,7 +261,12 @@ def _eval_subs(self, old, new): """ args = list(self.args) for i, (e, c) in enumerate(args): - e = e._subs(old, new) + try: + e = e._subs(old, new) + except TypeError: + if e != old: + continue + e = new if isinstance(c, bool): pass From e8bdc42668224a979d058aad0e523c76b096a331 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sun, 26 Feb 2012 08:58:43 +0545 Subject: [PATCH 047/104] pep8 cleanup of test_subs.py --- sympy/core/tests/test_subs.py | 239 +++++++++++++++++----------------- 1 file changed, 122 insertions(+), 117 deletions(-) diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 5db6ed01a9a1..2be30c91a218 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -6,14 +6,14 @@ 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(): @@ -21,12 +21,12 @@ 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 @@ -45,32 +45,32 @@ def test_trigonometric(): 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 @@ -79,20 +79,20 @@ def test_subbug2(): 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(r) == r[a]/r[b] * sin(r[b]*x) - assert e.subs(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(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 @@ -110,8 +110,8 @@ def test_dict_ambigous(): # see #467 assert g.subs(dg, warn=False) == 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 @@ -122,22 +122,22 @@ def test_dict_ambigous(): # see #467 raises(ValueError, 'x.subs({x: x**2 + 1, y: x + y})') 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 @@ -155,10 +155,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) @@ -184,7 +184,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) @@ -203,47 +204,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 @@ -259,56 +260,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 + 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) @@ -336,32 +338,35 @@ def test_add(): 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 + # 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 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') @@ -393,14 +398,14 @@ def test_subs_dict(): (sin(2*x), c), (sqrt(sin(2*x)), a), (cos(2*x), b), - (exp(x),e), + (exp(x), e), (x, d), ]) assert expr.subs(reps, warn=False) == 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 + 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 @@ -427,11 +432,11 @@ def test_no_arith_subs_on_floats(): @XFAIL def test_issue_2261() : - x = Symbol("x") + 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) + a, b, c, K = symbols('a b c K', commutative=True) assert (a/(b*c)).subs(b*c, K) == a/K assert (a/(b**2*c**3)).subs(b*c, K) == a/(c*K**2) From 811a6c27731f42ccdc0b5c35ab48e1f0b5698bba Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sun, 26 Feb 2012 16:29:36 +0545 Subject: [PATCH 048/104] remove ambiguity checking from subs --- sympy/core/basic.py | 49 ++++++++++------------------------- sympy/core/tests/test_subs.py | 18 +++++++------ 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index d24518c31805..af1cf2d109e7 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -783,9 +783,9 @@ def subs(self, *args, **kwargs): x**4 + y When unordered iterables are given they are sorted so a canonical result - is obtained, by count_op length and then by number of arguments to break - any ties. All other iterables are unsorted. A ValueError will be raised - if the substitution appears ambiguous unless flag ``warn=False``. + is obtained, by count_op length, number of arguments and, finally, 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 @@ -798,7 +798,7 @@ def subs(self, *args, **kwargs): >>> expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) - >>> expr.subs(dict([A,B,C,D,E]), warn=False) + >>> expr.subs(dict([A,B,C,D,E])) a*c*sin(d*e) + b """ @@ -841,37 +841,16 @@ def subs(self, *args, **kwargs): if unordered: sequence = dict(sequence) - def ambiguous(rules_dict): - """Return True if it appears that there could be ambiguity - in the substitution as a result of overlapping keys and - values.""" - val = set() - key = set() - for k, v in rules_dict.iteritems(): - kfree = _atomic(k) - # if x and cos(x) are keys, the result depends on - # which one gets replaced first - if any(ki in key for ki in kfree): - return True - # if any other substitution could introduce the key - # then the order will matter - if any(ki in val for ki in kfree): - return True - # if this substitution will introduce anything that - # has already been targeted before then its order matters - vfree = _atomic(v) - if any(vi in key for vi in vfree): - return True - val.update(vfree) - key.update(kfree) - if kwargs.pop('warn', True) and ambiguous(sequence): - raise ValueError('ambiguous unordered sequence %s; send items in a list' % sequence) - d = sift(sequence.iteritems(), lambda i: (i[0].count_ops(), len(i[0].args))) - 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 + if not all(k.is_Atom for k in sequence): + d = sift(sequence.iteritems(), lambda i: (i[0].count_ops(), len(i[0].args))) + 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) rv = self for old, new in sequence: diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 2be30c91a218..a4e4772fafed 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -106,20 +106,22 @@ def test_dict_ambigous(): # see #467 df= {x:y, exp(x): y} dg= {z:y, exp(z): y} - assert f.subs(df, warn=False) == y**2 - assert g.subs(dg, warn=False) == 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 # length of args and count_ops are the same so - # default sort key resolves ordering...if one + # 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}, warn=False) == 5 - raises(ValueError, 'x.subs({x: x**2 + 1, y: 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') @@ -389,8 +391,8 @@ def test_subs_dict(): assert (2*x + y + z).subs(dict(x=1, y=2)) == 4 + z l = [(sin(x), 2), (x, 1)] - assert (sin(x)).subs(l, warn=False) == \ - (sin(x)).subs(dict(l), warn=False) == 2 + 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) @@ -401,7 +403,7 @@ def test_subs_dict(): (exp(x), e), (x, d), ]) - assert expr.subs(reps, warn=False) == c + a*b*sin(d*e) + assert expr.subs(reps) == c + a*b*sin(d*e) l = [(x, 3), (y, x**2)] assert (x + y).subs(l) == 3 + x**2 From c291c347bd2571ae2dc2130563829ec4cbd547ca Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Wed, 29 Feb 2012 17:58:19 +0545 Subject: [PATCH 049/104] 2571: allow subs of integrand for indefinite integrals indefinite integral >>> i = Integral(x, x) >>> i.as_dummy() Integral(x, x) >>> i.subs(x, 1) Integral(1, x) "evaluate at" integral >>> at = Integral(x, (x, x)) >>> at.as_dummy() Integral(_x, (_x, x)) >>> at.subs(x, 1) Integral(x, (x, 1)) >>> i.doit() == at.doit() True >>> i.diff(x) == at.diff(x).doit() # get rid of Integral(0, (x, x)) True --- sympy/integrals/integrals.py | 153 ++++++++++++++++++------ sympy/integrals/tests/test_integrals.py | 9 +- 2 files changed, 123 insertions(+), 39 deletions(-) diff --git a/sympy/integrals/integrals.py b/sympy/integrals/integrals.py index a7fb672bf811..29cca3f4eef1 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 @@ -872,16 +956,11 @@ def _eval_subs(self, old, new): 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:]]) diff --git a/sympy/integrals/tests/test_integrals.py b/sympy/integrals/tests/test_integrals.py index 4bb297ea4e60..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 From d8b5e75ce7747f8eaeceb6ce9c93becf923fa44f Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 15 Mar 2012 00:32:01 +0545 Subject: [PATCH 050/104] subs gets parallel keyword This allows products to have all replacements made before evaluating and disallows subsequent substitutions from affecting former replacements (as happens fo serial susbtitutions). This is shown in the docstring. --- sympy/core/basic.py | 50 ++++++++++++++++++++++++++++------- sympy/core/tests/test_subs.py | 9 +++++++ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index af1cf2d109e7..acd1f80215ea 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -757,6 +757,9 @@ def subs(self, *args, **kwargs): resulting sorted list is then processed as an iterable container (see previous). + If the keyword ``parallel`` is True, the subexpressions will not be + evaluated until all the substitutions have been made. + Examples ======== @@ -782,10 +785,26 @@ def subs(self, *args, **kwargs): >>> (x**2 + x**4).xreplace({x**2: y}) x**4 + y - When unordered iterables are given they are sorted so a canonical result - is obtained, by count_op length, number of arguments and, finally, by - the default_sort_key to break any ties. All other iterables are - left unsorted + To delay evaluation until all substitutions have been made, + set the keyword ``parallel`` to True: + + >>> (x/y).subs([(x, 0), (y, 0)]) + 0 + >>> (x/y).subs([(x, 0), (y, 0)], parallel=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}, parallel=True) + y/(x + y) + + When unordered iterables are given they are sorted so a canonical + result is obtained, by count_op length, number of arguments and, + finally, 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 @@ -852,12 +871,23 @@ def subs(self, *args, **kwargs): sequence = sorted([(k, v) for (k, v) in sequence.iteritems()], key=default_sort_key) - rv = self - for old, new in sequence: - rv = rv._subs(old, new) - if not isinstance(rv, Basic): - break - return rv + if kwargs.pop('parallel', 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: + rv = self + for old, new in sequence: + rv = rv._subs(old, new) + if not isinstance(rv, Basic): + break + return rv @cacheit def _subs(self, old, new, **hints): diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index a4e4772fafed..51f1c220b471 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -473,3 +473,12 @@ def test_Piecewise_subs(): h = Function('h') p = Piecewise((g, x < -1), (g(x), x <= 1)) assert p.subs(g, h) == Piecewise((h, x < -1), (h(x), x <= 1)) + +def test_parallel_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, parallel=True) == (y/x).subs(reps, parallel=True) + reps = reps.items() + assert (x/y).subs(reps) != (y/x).subs(reps) + assert (x/y).subs(reps, parallel=True) == (y/x).subs(reps, parallel=True) From adad65dd1def72cae71276fede0c7e7b4f9f42dc Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 16 Mar 2012 20:13:50 +0545 Subject: [PATCH 051/104] parallel subs -> simultaneous subs --- sympy/core/basic.py | 18 +++++++++--------- sympy/core/tests/test_subs.py | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index acd1f80215ea..806b177af318 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -757,7 +757,7 @@ def subs(self, *args, **kwargs): resulting sorted list is then processed as an iterable container (see previous). - If the keyword ``parallel`` is True, the subexpressions will not be + If the keyword ``simultaneous`` is True, the subexpressions will not be evaluated until all the substitutions have been made. Examples @@ -786,11 +786,11 @@ def subs(self, *args, **kwargs): x**4 + y To delay evaluation until all substitutions have been made, - set the keyword ``parallel`` to True: + set the keyword ``simultaneous`` to True: >>> (x/y).subs([(x, 0), (y, 0)]) 0 - >>> (x/y).subs([(x, 0), (y, 0)], parallel=True) + >>> (x/y).subs([(x, 0), (y, 0)], simultaneous=True) nan This has the added feature of not allowing subsequent substitutions @@ -798,13 +798,13 @@ def subs(self, *args, **kwargs): >>> ((x + y)/y).subs({x + y: y, y: x + y}) 1 - >>> ((x + y)/y).subs({x + y: y, y: x + y}, parallel=True) + >>> ((x + y)/y).subs({x + y: y, y: x + y}, simultaneous=True) y/(x + y) - When unordered iterables are given they are sorted so a canonical - result is obtained, by count_op length, number of arguments and, - finally, by the default_sort_key to break any ties. All other - iterables are left unsorted + 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 @@ -871,7 +871,7 @@ def subs(self, *args, **kwargs): sequence = sorted([(k, v) for (k, v) in sequence.iteritems()], key=default_sort_key) - if kwargs.pop('parallel', False): # XXX should this be the default for dict subs? + if kwargs.pop('simultaneous', False): # XXX should this be the default for dict subs? reps = {} rv = self for old, new in sequence: diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 51f1c220b471..1d453173f96b 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -474,11 +474,11 @@ def test_Piecewise_subs(): p = Piecewise((g, x < -1), (g(x), x <= 1)) assert p.subs(g, h) == Piecewise((h, x < -1), (h(x), x <= 1)) -def test_parallel_subs(): +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, parallel=True) == (y/x).subs(reps, parallel=True) + 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, parallel=True) == (y/x).subs(reps, parallel=True) + assert (x/y).subs(reps, simultaneous=True) == (y/x).subs(reps, simultaneous=True) From 1962af31b597b259c025b96d317c50faedc4299a Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sat, 17 Mar 2012 01:47:30 +0545 Subject: [PATCH 052/104] 1102: f(x).subs(f,h) -> h(x) --- sympy/core/basic.py | 8 +++++++- sympy/core/tests/test_subs.py | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index 806b177af318..82875bdc54c2 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -861,7 +861,13 @@ def subs(self, *args, **kwargs): if unordered: sequence = dict(sequence) if not all(k.is_Atom for k in sequence): - d = sift(sequence.iteritems(), lambda i: (i[0].count_ops(), len(i[0].args))) + 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)) diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 1d453173f96b..95fe5dfb8036 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -467,12 +467,12 @@ def test_issue_3059(): assert (x - oo).subs(oo, y) == x - y assert (x - oo).subs(-oo, y) == x + y -def test_Piecewise_subs(): +def test_Function_subs(): from sympy.abc import x, y - g = Function('g') - h = Function('h') + 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 From 8022a1069df7dc6873dce21b2cb4a8ace3129589 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Sat, 17 Mar 2012 04:38:21 +0530 Subject: [PATCH 053/104] Run script on sympy/ if no arguments provided --- bin/coverage_doctest.py | 56 +++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 26a6bec17a64..572433659742 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -225,11 +225,11 @@ def process_class(c_name, obj, c_md, c_mdt, c_idt, c_has_doctest): c_dt = False # Get the line number of class try: - line_no = inspect.getsourcelines(obj)[1] + source, line_no = inspect.getsourcelines(obj) except IOError: # Raised when source does not exist # which means the class is not there. - return c_dt, c + return False, False, None c = True full_name = "LINE %d: %s" % (line_no, c_name) @@ -237,10 +237,11 @@ def process_class(c_name, obj, c_md, c_mdt, c_idt, c_has_doctest): elif not '>>>' in obj.__doc__: c_mdt.append(full_name) elif _is_indirect(c_name, obj.__doc__): c_idt.append(full_name) - else: # indirect doctest + else: c_dt = True c_has_doctest.append(full_name) - return c_dt, c + + return c_dt, c, source def coverage(module_path, verbose=False): @@ -304,14 +305,18 @@ def coverage(module_path, verbose=False): elif inspect.isclass(obj): # Process the class first - c_dt, c = process_class(member, obj, c_md, c_mdt, c_idt, c_has_doctest) - if c: classes += 1 + c_dt, c, source = process_class(member, obj, 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: continue + 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) @@ -391,22 +396,23 @@ def go(sympy_top, file, verbose=False, exact=True): 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: - file = os.path.normpath(file) - print 'DOCTEST COVERAGE for %s' % (file) - print '='*70 - print - doctests, num_functions = go(sympy_top, file, options.verbose) - if num_functions == 0: - score = 100 - else: - score = 100 * float(doctests) / num_functions - score = int(score) - print - print '='*70 - print "TOTAL SCORE for %s: %s%% (%s of %s)" % \ - (get_mod_name(file, sympy_top), score, doctests, num_functions) - print + 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) + if num_functions == 0: + score = 100 + else: + score = 100 * float(doctests) / num_functions + score = int(score) + print + print '='*70 + print "TOTAL SCORE for %s: %s%% (%s of %s)" % \ + (get_mod_name(file, sympy_top), score, doctests, num_functions) + print From 402d991f825208a76fccce5e596bb9ecb208267e Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Sat, 17 Mar 2012 04:47:34 +0530 Subject: [PATCH 054/104] Include warning about indirect doctest in verbose output --- bin/coverage_doctest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 572433659742..f77519ea73fa 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -64,6 +64,7 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc 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','*') From 40b17731ecbc1b10b83709d5f44d0e735228fa1e Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Fri, 16 Mar 2012 16:36:03 -0700 Subject: [PATCH 055/104] Fixed typo in test --- sympy/matrices/expressions/tests/test_matrix_exprs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sympy/matrices/expressions/tests/test_matrix_exprs.py b/sympy/matrices/expressions/tests/test_matrix_exprs.py index b369049dd572..070090bb441a 100644 --- a/sympy/matrices/expressions/tests/test_matrix_exprs.py +++ b/sympy/matrices/expressions/tests/test_matrix_exprs.py @@ -317,7 +317,7 @@ def test_matrixify(): assert matrixify(n+m) == n+m assert matrixify(Mul(A,B)) == MatMul(A,B) -def test_dense_converstion(): +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]]) From 44407c107d381254ba379c10c78f8c7bfc758a51 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Sat, 17 Mar 2012 05:11:22 +0530 Subject: [PATCH 056/104] Add color coding to doctest coverage percentage Using two colors for 100% and < 100%, Green and Red respectively --- bin/coverage_doctest.py | 53 +++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index f77519ea73fa..986e8f9e6c5c 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -24,8 +24,36 @@ import inspect from optparse import OptionParser +# 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) @@ -34,15 +62,19 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc """ Prints details (depending on verbose) of a module """ + if 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%% (%s of %s)" % (module_path, score, total_doctests, total_members) - - - + print "%s: %s" % (module_path, score_string) if verbose: print_header('CLASSES', '*') @@ -86,7 +118,7 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc if verbose: print '\n'+'-'*70 - print "SCORE: %s%% (%s of %s)" % (score, total_doctests, total_members) + print "SCORE: %s" % (score_string) print '-'*70 @@ -414,6 +446,13 @@ def go(sympy_top, file, verbose=False, exact=True): score = int(score) print print '='*70 - print "TOTAL SCORE for %s: %s%% (%s of %s)" % \ - (get_mod_name(file, sympy_top), score, doctests, num_functions) + if 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 From 6f65787f8b932f5c36dafee2e9971055f9687581 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Sat, 17 Mar 2012 05:23:13 +0530 Subject: [PATCH 057/104] If there is nothing to document, 0 out of 0 should be 100% --- bin/coverage_doctest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 986e8f9e6c5c..133a049d7308 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -371,7 +371,7 @@ def coverage(module_path, verbose=False): total_doctests = c_doctests + f_doctests total_members = classes + functions if total_members: score = 100 * float(total_doctests) / (total_members) - else: score = 0 + else: score = 100 score = int(score) # Sort functions/classes by line number @@ -411,6 +411,7 @@ def go(sympy_top, file, verbose=False, exact=True): 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/ From 12f7a4821462ae4cab44f9efcc5bbb537862e749 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Sat, 17 Mar 2012 05:32:09 +0530 Subject: [PATCH 058/104] Add "# indirect doctest" note for Functions too --- bin/coverage_doctest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 133a049d7308..2f525b3c3e17 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -115,6 +115,8 @@ def print_coverage(module_path, c, c_md, c_mdt, c_idt, f, f_md, f_mdt, f_idt, sc 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 From 3c1b1e60ffe6b9a295d7c59fe8bcf4fe69a3edad Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Sat, 17 Mar 2012 05:50:22 +0530 Subject: [PATCH 059/104] Skip classes (and their members) if it starts with underscore --- bin/coverage_doctest.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 2f525b3c3e17..1011fd9d8fb5 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -250,12 +250,17 @@ def process_function(name, c_name, b_obj, mod_path, f_sk, f_md, f_mdt, f_idt, f_ return f_doctest, function -def process_class(c_name, obj, c_md, c_mdt, c_idt, c_has_doctest): +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 @@ -340,7 +345,7 @@ def coverage(module_path, verbose=False): elif inspect.isclass(obj): # Process the class first - c_dt, c, source = process_class(member, obj, c_md, c_mdt, c_idt, c_has_doctest) + 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 From 871567e64865ea70cc5f71cc8db8a7ae24dc2ec1 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Sat, 17 Mar 2012 06:07:46 +0530 Subject: [PATCH 060/104] Add --no-color option to the parser Sometimes terminals might not support colors, in that case --no-color should be used to avoid garbled output due to color formatting strings. --- bin/coverage_doctest.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/bin/coverage_doctest.py b/bin/coverage_doctest.py index 1011fd9d8fb5..e4b2f7faee55 100755 --- a/bin/coverage_doctest.py +++ b/bin/coverage_doctest.py @@ -50,19 +50,21 @@ 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): +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 score < 100: + 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: @@ -283,7 +285,7 @@ def process_class(c_name, obj, c_sk, c_md, c_mdt, c_idt, c_has_doctest): return c_dt, c, source -def coverage(module_path, verbose=False): +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 @@ -390,17 +392,17 @@ def coverage(module_path, verbose=False): 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) + 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, exact=True): +def go(sympy_top, file, verbose=False, no_color=False, exact=True): if os.path.isdir(file): doctests, num_functions = 0, 0 for F in os.listdir(file): - _doctests, _num_functions = go(sympy_top, '%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 @@ -413,7 +415,7 @@ def go(sympy_top, file, verbose=False, exact=True): sys.exit(1) # Relpath for constructing the module name - return coverage(get_mod_name(file, sympy_top), verbose) + return coverage(get_mod_name(file, sympy_top), verbose, no_color) if __name__ == "__main__": @@ -434,6 +436,7 @@ def go(sympy_top, file, verbose=False, exact=True): 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() @@ -446,7 +449,7 @@ def go(sympy_top, file, verbose=False, exact=True): print 'DOCTEST COVERAGE for %s' % (file) print '='*70 print - doctests, num_functions = go(sympy_top, file, options.verbose) + doctests, num_functions = go(sympy_top, file, options.verbose, options.no_color) if num_functions == 0: score = 100 else: @@ -454,10 +457,16 @@ def go(sympy_top, file, verbose=False, exact=True): score = int(score) print print '='*70 - if score < 100: + + if options.no_color: + print "TOTAL SCORE for %s: %s%% (%s of %s)" % \ + (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"]),\ From f86720b1c929383db9215f2cd3db6adb85b5cc26 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 16 Mar 2012 18:11:53 -0600 Subject: [PATCH 061/104] Add Sanket Agarwal to the AUTHORS/aboutus. Welcome to SymPy! --- AUTHORS | 1 + doc/src/aboutus.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 27aefbfac116..c908ddde3df2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -191,3 +191,4 @@ Carsten Knoll M R Bharath Matthias Toews Jorge E. Cardona +Sanket Agarwal diff --git a/doc/src/aboutus.txt b/doc/src/aboutus.txt index fddf058c59f8..98690f248af3 100644 --- a/doc/src/aboutus.txt +++ b/doc/src/aboutus.txt @@ -198,6 +198,7 @@ want to be mentioned here, so see our repository history for a full list). #. M R Bharath: modified use of int_tested #. Matthias Toews: File permissions #. Jorge E. Cardona: Cleanup in polys +#. Sanket Agarwal: Rewrite coverage_doctest.py script Up-to-date list in the order of the first contribution is given in the `AUTHORS `_ file. From 126f7634c7be340129b4dd9e44b4d3b782fd74db Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sat, 17 Mar 2012 07:04:01 +0545 Subject: [PATCH 062/104] 2552: handle numerator and exit if no longer a Mul --- sympy/core/mul.py | 10 ++++++---- sympy/core/tests/test_subs.py | 5 +++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/sympy/core/mul.py b/sympy/core/mul.py index c364a648ad4e..361b34d02aa5 100644 --- a/sympy/core/mul.py +++ b/sympy/core/mul.py @@ -1213,10 +1213,12 @@ def ndiv(a, b): rv = None n, d = self.as_numer_denom() if d is not S.One: - was = self - self = n/d._subs(old, new) - if self != was: - rv = self + 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 diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 95fe5dfb8036..5e2c5f00cae4 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -439,8 +439,13 @@ def test_issue_2261() : 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) From 5e8e547cd66b35049add1c6400e9dacd508abc82 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sat, 17 Mar 2012 07:37:26 +0545 Subject: [PATCH 063/104] edit replace, subs, and xreplace docstrings --- sympy/core/basic.py | 88 ++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/sympy/core/basic.py b/sympy/core/basic.py index 82875bdc54c2..d9acf2ed6966 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -820,6 +820,13 @@ def subs(self, *args, **kwargs): >>> 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 @@ -1005,18 +1012,18 @@ 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, exp - >>> x,y, z = symbols('x y z') + >>> x, y, z = symbols('x y z') >>> (1 + x*y).xreplace({x: pi}) pi*y + 1 >>> (1 + x*y).xreplace({x:pi, y:2}) @@ -1051,6 +1058,13 @@ def xreplace(self, rule): >>> 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] @@ -1174,46 +1188,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): From 93ec1fb961b672fdc05b56896658fb2e33dffbee Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 15 Mar 2012 16:23:28 +0545 Subject: [PATCH 064/104] SymPy round function -> method Unlike the round function of Python, the round method of SymPy can be applied to a complex value: the real and imaginary components will be rounded to the same decimal place, p, which is similar to how evalf behaves wrt significant digits. --- doc/src/modules/evalf.txt | 18 ++-- sympy/assumptions/handlers/ntheory.py | 5 +- sympy/assumptions/handlers/sets.py | 3 +- sympy/core/expr.py | 96 ++++++++++++++++++- sympy/core/numbers.py | 6 +- sympy/core/tests/test_evalf.py | 2 +- sympy/core/tests/test_expr.py | 76 ++++++++++++++- sympy/core/tests/test_numbers.py | 14 +-- sympy/functions/__init__.py | 2 +- sympy/functions/elementary/miscellaneous.py | 70 -------------- .../elementary/tests/test_complexes.py | 2 +- .../elementary/tests/test_miscellaneous.py | 64 +------------ sympy/printing/tests/test_str.py | 6 +- 13 files changed, 197 insertions(+), 167 deletions(-) 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/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/core/expr.py b/sympy/core/expr.py index 7298882fa650..a228f99d114c 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 @@ -2653,6 +2656,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. @@ -2687,3 +2778,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/numbers.py b/sympy/core/numbers.py index f2adde8c06d2..cd327375b541 100644 --- a/sympy/core/numbers.py +++ b/sympy/core/numbers.py @@ -223,6 +223,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' % \ @@ -817,9 +820,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) 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 8e3303845aee..a454124a1921 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 @@ -1281,3 +1281,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/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/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/tests/test_complexes.py b/sympy/functions/elementary/tests/test_complexes.py index 152ea6db1e41..f3bf3e0ff1ab 100644 --- a/sympy/functions/elementary/tests/test_complexes.py +++ b/sympy/functions/elementary/tests/test_complexes.py @@ -112,7 +112,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; 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/printing/tests/test_str.py b/sympy/printing/tests/test_str.py index 1cd9e44fb9e8..eb35d547460e 100644 --- a/sympy/printing/tests/test_str.py +++ b/sympy/printing/tests/test_str.py @@ -4,7 +4,7 @@ factorial, factorial2, Function, GoldenRatio, I, Integer, Integral, Interval, Lambda, Limit, log, Matrix, nan, O, oo, pi, Rational, Float, 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 @@ -302,8 +302,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" From a2db9e02d57f4578be7b301450bd9e117e141f27 Mon Sep 17 00:00:00 2001 From: Manoj Babu Date: Sat, 17 Mar 2012 18:56:36 +0530 Subject: [PATCH 065/104] Gamma Function: Simplified output for Rational Inputs I have run all the tests.Result is positive and also removed white spaces before and after = at expand(func = True).Dummy variable x created. --- sympy/functions/special/gamma_functions.py | 7 +++++++ sympy/functions/special/tests/test_gamma_functions.py | 6 ++++++ 2 files changed, 13 insertions(+) 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_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) From 052ce3b27710bdbdd857f1933ca9c5f75b1de9b1 Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Mon, 20 Feb 2012 11:28:28 +0000 Subject: [PATCH 066/104] sympy/core/sets.py: use attributes instead of properties where possible NB: Set.is_real has to be a property to prevent the assumption system from taking it over. --- sympy/core/sets.py | 62 ++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index a083bb922ebe..f5f8cfd47e85 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -17,6 +17,14 @@ class Set(Basic): 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 def union(self, other): """ @@ -206,29 +214,8 @@ def __contains__(self, other): return result @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): """ @@ -267,6 +254,7 @@ class ProductSet(Set): - Passes most operations down to the argument sets - Flattens Products of ProductSets """ + is_ProductSet = True def __new__(cls, *sets, **assumptions): def flatten(arg): @@ -355,30 +343,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") @@ -413,6 +393,7 @@ class Interval(RealSet): - Use the evalf() method to turn an Interval into an mpmath 'mpi' interval instance """ + is_Interval = True def __new__(cls, start, end, left_open=False, right_open=False): @@ -590,9 +571,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): @@ -648,9 +626,9 @@ class Union(Set): [1, 3] """ + is_Union = True def __new__(cls, *args): - # Flatten out Iterators and Unions to form one list of sets args = list(args) def flatten(arg): @@ -810,9 +788,6 @@ def as_relational(self, symbol): def is_iterable(self): return all(arg.is_iterable for arg in self.args) - @property - def is_Union(self): - return True class RealUnion(Union, RealSet): """ @@ -946,7 +921,6 @@ class EmptySet(Set): EmptySet() """ - __metaclass__ = Singleton def _intersect(self, other): @@ -990,6 +964,8 @@ class FiniteSet(CountableSet): True """ + is_FiniteSet = True + def __new__(cls, *args): def flatten(arg): if is_flattenable(arg): @@ -1050,9 +1026,7 @@ def union(self, other): >>> Interval(1, 2) - FiniteSet(2, 3) [1, 2) - """ - if other == S.EmptySet: return self if other.is_FiniteSet: @@ -1098,10 +1072,6 @@ def as_relational(self, symbol): 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) From bcf1adc2c04a3e9ed967cd744028288ab6192d04 Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Mon, 20 Feb 2012 14:22:50 +0000 Subject: [PATCH 067/104] Use ask() in Set.__contains__() Set.contains should return symbolic results, so __contains__ needs to use ask() to convert it into a concrete True/False value. --- sympy/core/sets.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index f5f8cfd47e85..712b58fb80a8 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -1,11 +1,12 @@ -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 class Set(Basic): """ @@ -208,9 +209,10 @@ 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 @property From a6aaced699985a3ecec7eb5a5b51945e015c90f9 Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Mon, 20 Feb 2012 17:08:46 +0000 Subject: [PATCH 068/104] sympy/core/sets.py: import And, Or at module scope. --- sympy/core/sets.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index 712b58fb80a8..9a02456d5ab4 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -7,6 +7,7 @@ from sympy.mpmath import mpi, mpf from sympy.assumptions import ask +from sympy.logic.boolalg import And, Or class Set(Basic): """ @@ -292,7 +293,6 @@ def _contains(self, element): if len(element) != len(self.args): return False - from sympy.logic.boolalg import And return And(*[set.contains(item) for set,item in zip(self.sets,element)]) def _intersect(self, other): @@ -537,7 +537,6 @@ def _complement(self): 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: @@ -587,7 +586,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: @@ -732,7 +730,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) @@ -783,7 +780,6 @@ def _measure(self): def as_relational(self, symbol): """Rewrite a Union in terms of equalities and logic operators. """ - from sympy.logic.boolalg import Or return Or(*[set.as_relational(symbol) for set in self.args]) @property @@ -1071,7 +1067,6 @@ def as_relational(self, symbol): """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 @@ -1122,7 +1117,6 @@ def as_relational(self, symbol): """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__ From 9e1b6b6bd1fe99545c0f6ac012ab1ffc7c8d8c77 Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Wed, 22 Feb 2012 02:44:22 +0000 Subject: [PATCH 069/104] Make _sympify((0, 0)) work. --- sympy/core/containers.py | 6 ++++-- sympy/core/sympify.py | 4 ---- sympy/core/tests/test_arit.py | 1 + 3 files changed, 5 insertions(+), 6 deletions(-) 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/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_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): From 5e1b635e3c806c250dbce6c381572b3cc9a69982 Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Wed, 22 Feb 2012 19:52:09 +0000 Subject: [PATCH 070/104] Simplify arg sympification in FiniteSet --- sympy/core/sets.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index 9a02456d5ab4..fafca59f7225 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -971,10 +971,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() @@ -985,7 +982,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 From ec9094f69faab01ad1baf62fb54e11988d936795 Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Wed, 22 Feb 2012 21:35:35 +0000 Subject: [PATCH 071/104] Handle sympification in Set.contains Calling sympify in Set.contains() instead of ._contains() ensures consistent behavior between all subclasses of Set. As sympification has been made strict, non-Basic objects in FiniteSet aren't supported any more. --- sympy/core/sets.py | 13 +++---------- sympy/core/tests/test_sets.py | 6 ++++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index fafca59f7225..c67fe2293f44 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -142,7 +142,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)) @@ -293,7 +293,7 @@ def _contains(self, element): if len(element) != len(self.args): return False - 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: @@ -535,13 +535,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. - try: - other = _sympify(other) - except SympifyError: - return False - if self.left_open: expr = other > self.start else: @@ -1042,7 +1035,7 @@ def _contains(self, other): False """ - return sympify(other) in self.elements + return other in self.elements @property def _inf(self): diff --git a/sympy/core/tests/test_sets.py b/sympy/core/tests/test_sets.py index 1feace034032..6ddbb0f3e38c 100644 --- a/sympy/core/tests/test_sets.py +++ b/sympy/core/tests/test_sets.py @@ -244,9 +244,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 From 5227501fadf9faaa1ad383d12405027d7d483d53 Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Sat, 17 Mar 2012 12:07:54 -0700 Subject: [PATCH 072/104] Added Intersection class Also added UniverseSet --- sympy/core/__init__.py | 3 +- sympy/core/sets.py | 189 +++++++++++++++++++++++++++++----- sympy/core/tests/test_sets.py | 1 - 3 files changed, 163 insertions(+), 30 deletions(-) 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/sets.py b/sympy/core/sets.py index c67fe2293f44..1436a3330ae9 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -27,6 +27,8 @@ class Set(Basic): is_Interval = False is_ProductSet = False is_Union = False + is_Intersection = None + is_EmptySet = None def union(self, other): """ @@ -63,6 +65,7 @@ def intersect(self, other): [1, 2] """ + return Intersection(self, other) return self._intersect(other) def _intersect(self, other): @@ -299,10 +302,12 @@ 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 + # raise TypeError("%s is not a Product Set."%str(other)) 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 None + # raise ValueError("Sets not the same size Left: %d, Right: %d" + # %(len(self.args), len(other.args))) return ProductSet(a.intersect(b) for a, b in zip(self.sets, other.sets)) @@ -487,12 +492,12 @@ def right_open(self): return self._args[3] def _intersect(self, other): - if not isinstance(other, Interval): - return other.intersect(self) - + if not other.is_Interval: + return None if not self._is_comparable(other): - raise NotImplementedError("Intersection of intervals with symbolic " - "end points is not yet implemented") + return None + #raise NotImplementedError("Intersection of intervals with symbolic " +# "end points is not yet implemented") empty = False @@ -694,26 +699,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. @@ -780,6 +765,112 @@ def is_iterable(self): return all(arg.is_iterable for arg in self.args) +class Intersection(Set): + 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.UniverseSet + + # Reduce sets using known rules + if evaluate: + return Intersection.reduce(args) + + return Basic.__new__(cls, *args) + + @property + def is_iterable(self): + return all(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]) + + @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): """ Represents a union of Real Sets (Intervals, RealFiniteSets) @@ -913,6 +1004,7 @@ class EmptySet(Set): """ __metaclass__ = Singleton + is_EmptySet = True def _intersect(self, other): return S.EmptySet @@ -940,6 +1032,47 @@ def union(self, other): def __iter__(self): return iter([]) +class UniverseSet(Set): + """ + Represents the set of all things. + The universe set is available as a singleton as S.UniverseSet + + Examples + ======== + + >>> from sympy import S, Interval + + >>> S.UniverseSet + UniverseSet() + + >>> Interval(1, 2).intersect(S.UniverseSet) + Interval(1,2) + + """ + + __metaclass__ = Singleton + is_UniverseSet = 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 diff --git a/sympy/core/tests/test_sets.py b/sympy/core/tests/test_sets.py index 6ddbb0f3e38c..3682b40e780d 100644 --- a/sympy/core/tests/test_sets.py +++ b/sympy/core/tests/test_sets.py @@ -85,7 +85,6 @@ 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)") def test_difference(): From 0d2a95efe76b9734c7293d0246836cf1ae5a7e31 Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Sat, 17 Mar 2012 12:32:37 -0700 Subject: [PATCH 073/104] Added docstrings and in-line comments --- sympy/core/sets.py | 62 +++++++++++++++++++++++------------ sympy/core/tests/test_sets.py | 5 +-- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index 1436a3330ae9..a991afe6badd 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -29,6 +29,7 @@ class Set(Basic): is_Union = False is_Intersection = None is_EmptySet = None + is_UniversalSet = None def union(self, other): """ @@ -66,9 +67,19 @@ def intersect(self, other): """ return Intersection(self, other) - return self._intersect(other) def _intersect(self, other): + """ + This class 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 + """ raise NotImplementedError("(%s)._intersect(%s)" % (self, other)) @property @@ -299,15 +310,10 @@ def _contains(self, 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: return None - # raise TypeError("%s is not a Product Set."%str(other)) if len(other.args) != len(self.args): - return None - # 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)) @@ -321,13 +327,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) @@ -492,12 +496,12 @@ def right_open(self): return self._args[3] def _intersect(self, other): + # 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): return None - #raise NotImplementedError("Intersection of intervals with symbolic " -# "end points is not yet implemented") empty = False @@ -766,6 +770,22 @@ def is_iterable(self): 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] + """ is_Intersection = True def __new__(cls, *args, **kwargs): @@ -786,7 +806,7 @@ def flatten(arg): # Intersection of no sets is everything if len(args)==0: - return S.UniverseSet + return S.UniversalSet # Reduce sets using known rules if evaluate: @@ -1011,7 +1031,7 @@ def _intersect(self, other): @property def _complement(self): - return Interval(S.NegativeInfinity, S.Infinity) + return S.UniversalSet @property def _measure(self): @@ -1032,26 +1052,26 @@ def union(self, other): def __iter__(self): return iter([]) -class UniverseSet(Set): +class UniversalSet(Set): """ Represents the set of all things. - The universe set is available as a singleton as S.UniverseSet + The universal set is available as a singleton as S.UniversalSet Examples ======== >>> from sympy import S, Interval - >>> S.UniverseSet - UniverseSet() + >>> S.UniversalSet + UniversalSet() - >>> Interval(1, 2).intersect(S.UniverseSet) - Interval(1,2) + >>> Interval(1, 2).intersect(S.UniversalSet) + [1, 2] """ __metaclass__ = Singleton - is_UniverseSet = True + is_UniversalSet = True def _intersect(self, other): return other diff --git a/sympy/core/tests/test_sets.py b/sympy/core/tests/test_sets.py index 3682b40e780d..6d9ad1083e46 100644 --- a/sympy/core/tests/test_sets.py +++ b/sympy/core/tests/test_sets.py @@ -116,7 +116,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), @@ -373,7 +374,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 From 4f62f68f9ddbe896bd86c4ea38771cdb140ff558 Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Sat, 17 Mar 2012 12:37:15 -0700 Subject: [PATCH 074/104] added wikipedia links and see also sections --- sympy/core/sets.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index a991afe6badd..1c328208bd96 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -270,6 +270,8 @@ class ProductSet(Set): Notes: - Passes most operations down to the argument sets - Flattens Products of ProductSets + + http://en.wikipedia.org/wiki/Cartesian_product """ is_ProductSet = True @@ -403,6 +405,8 @@ class Interval(RealSet): - 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 + + http://en.wikipedia.org/wiki/Interval_(mathematics) """ is_Interval = True @@ -627,6 +631,11 @@ class Union(Set): >>> Union(Interval(1, 2), Interval(2, 3)) [1, 3] + See Also + ======== + Intersection + + http://en.wikipedia.org/wiki/Union_(set_theory) """ is_Union = True @@ -785,6 +794,12 @@ class Intersection(Set): >>> Interval(1,3).intersect(Interval(2,4)) [2, 3] + + See Also + ======== + Union + + http://en.wikipedia.org/wiki/Intersection_(set_theory) """ is_Intersection = True @@ -1022,6 +1037,11 @@ class EmptySet(Set): >>> Interval(1, 2).intersect(S.EmptySet) EmptySet() + See Also + ======== + UniversalSet + + http://en.wikipedia.org/wiki/Empty_set """ __metaclass__ = Singleton is_EmptySet = True @@ -1068,6 +1088,11 @@ class UniversalSet(Set): >>> Interval(1, 2).intersect(S.UniversalSet) [1, 2] + See Also + ======== + EmptySet + + http://en.wikipedia.org/wiki/Universal_set """ __metaclass__ = Singleton @@ -1107,6 +1132,7 @@ class FiniteSet(CountableSet): >>> 3 in FiniteSet(1, 2, 3, 4) True + http://en.wikipedia.org/wiki/Finite_set """ is_FiniteSet = True From c5e51847afabcc35daf27c41cf332116b82b0b4b Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Sat, 17 Mar 2012 13:04:51 -0700 Subject: [PATCH 075/104] added printing and test_args --- sympy/core/tests/test_args.py | 4 ++++ sympy/printing/latex.py | 3 +++ sympy/printing/pretty/pretty.py | 11 +++++++++-- sympy/printing/pretty/pretty_symbology.py | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sympy/core/tests/test_args.py b/sympy/core/tests/test_args.py index 0857005558c7..4d78e2316692 100644 --- a/sympy/core/tests/test_args.py +++ b/sympy/core/tests/test_args.py @@ -343,6 +343,10 @@ 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))) + 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/printing/latex.py b/sympy/printing/latex.py index c5a1052f29bf..005976c734eb 100644 --- a/sympy/printing/latex.py +++ b/sympy/printing/latex.py @@ -1060,6 +1060,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 fda55646d84c..359fc1ed78be 100644 --- a/sympy/printing/pretty/pretty.py +++ b/sympy/printing/pretty/pretty.py @@ -1127,7 +1127,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: @@ -1161,12 +1161,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): From c8e846a2fb499e24e60cfa045bc649a5d257a49e Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Sat, 17 Mar 2012 15:21:01 -0700 Subject: [PATCH 076/104] Improved testing. Added iteration of Intersections --- sympy/core/sets.py | 19 ++++++++++++++---- sympy/core/tests/test_args.py | 7 ++++++- sympy/core/tests/test_sets.py | 36 ++++++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index 1c328208bd96..6a2290762180 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -70,7 +70,7 @@ def intersect(self, other): def _intersect(self, other): """ - This class should only be used internally + 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 @@ -306,8 +306,10 @@ 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 return And(*[set.contains(item) for set, item in zip(self.sets, element)]) @@ -831,7 +833,7 @@ def flatten(arg): @property def is_iterable(self): - return all(arg.is_iterable for arg in self.args) + return any(arg.is_iterable for arg in self.args) @property def _inf(self): @@ -849,6 +851,15 @@ 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): """ diff --git a/sympy/core/tests/test_args.py b/sympy/core/tests/test_args.py index 4d78e2316692..55b306a12b41 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)) @@ -345,7 +349,8 @@ def test_sympy__core__sets__Set(): def test_sympy__core__sets__Intersection(): from sympy.core.sets import Intersection, Interval - assert _test_args(Intersection(Interval(0, 3), Interval(2, 4))) + 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 diff --git a/sympy/core/tests/test_sets.py b/sympy/core/tests/test_sets.py index 6d9ad1083e46..4ca0974bef75 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) @@ -87,6 +86,8 @@ def test_union(): 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) @@ -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) + assert (2,2) not in i + assert 5 not in i + raises(ValueError, "list(i)") + def test_interval_subs(): a = Symbol('a', real=True) @@ -357,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 @@ -410,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 From 8c3f255f3dd48edb26c89685b54ddc6034e7fc7e Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Sat, 17 Mar 2012 15:48:05 -0700 Subject: [PATCH 077/104] default behavior for _intersect is to return None --- sympy/core/sets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index 6a2290762180..dacef16e19e0 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -80,7 +80,7 @@ def _intersect(self, other): Used within the Intersection class """ - raise NotImplementedError("(%s)._intersect(%s)" % (self, other)) + return None @property def complement(self): From 1e94eb8f9b02b3992b74d77c6dc18067898aa849 Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Sat, 17 Mar 2012 19:48:46 -0700 Subject: [PATCH 078/104] updated test after merger with contains branch --- sympy/core/tests/test_sets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sympy/core/tests/test_sets.py b/sympy/core/tests/test_sets.py index 4ca0974bef75..8517799be072 100644 --- a/sympy/core/tests/test_sets.py +++ b/sympy/core/tests/test_sets.py @@ -192,9 +192,9 @@ def test_intersection(): # Products line = Interval(0, 5) - i = Intersection(line**2, line) + i = Intersection(line**2, line**3, evaluate=False) assert (2,2) not in i - assert 5 not in i + assert (2,2,2) not in i raises(ValueError, "list(i)") def test_interval_subs(): From 2f7a1f5faa7860fdbbc7a4bd08829fb9d5600e02 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sun, 18 Mar 2012 10:11:22 +0545 Subject: [PATCH 079/104] do not sort Implies arguments --- sympy/printing/pretty/pretty.py | 8 ++++++-- sympy/printing/pretty/tests/test_pretty.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/sympy/printing/pretty/pretty.py b/sympy/printing/pretty/pretty.py index 168a321d8944..19a1de0b0659 100644 --- a/sympy/printing/pretty/pretty.py +++ b/sympy/printing/pretty/pretty.py @@ -133,7 +133,10 @@ def _print_Not(self, e): return self._print_Function(e) def __print_Boolean(self, e, char): - args = sorted(e.args, key=default_sort_key) + if isinstance(e, Basic): + args = sorted(e.args, key=default_sort_key) + else: + args = e arg = args[0] pform = self._print(arg) @@ -183,7 +186,8 @@ def _print_Nor(self, e): def _print_Implies(self, e): if self._use_unicode: - return self.__print_Boolean(e, u"\u2192") + # don't sort implies' args + return self.__print_Boolean(e.args, u"\u2192") else: return self._print_Function(e) diff --git a/sympy/printing/pretty/tests/test_pretty.py b/sympy/printing/pretty/tests/test_pretty.py index 4dc99ceea971..3b05f0e25fa7 100644 --- a/sympy/printing/pretty/tests/test_pretty.py +++ b/sympy/printing/pretty/tests/test_pretty.py @@ -2596,11 +2596,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) From 19790474e583322b7c318f156cdad486bed1f7cc Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Sat, 17 Mar 2012 23:22:15 -0600 Subject: [PATCH 080/104] Update aboutus file --- doc/src/aboutus.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/aboutus.txt b/doc/src/aboutus.txt index 98690f248af3..e2364af013c0 100644 --- a/doc/src/aboutus.txt +++ b/doc/src/aboutus.txt @@ -195,7 +195,7 @@ 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 From 8451ce5884a08db24e7ba746253c0342ba5bd8ae Mon Sep 17 00:00:00 2001 From: Tom Bachmann Date: Sun, 18 Mar 2012 11:52:48 +0000 Subject: [PATCH 081/104] Add Manoj Babu K. to AUTHORS. Welcome to sympy! --- AUTHORS | 1 + doc/src/aboutus.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index c908ddde3df2..a68bbe7d9d66 100644 --- a/AUTHORS +++ b/AUTHORS @@ -192,3 +192,4 @@ M R Bharath Matthias Toews Jorge E. Cardona Sanket Agarwal +Manoj Babu K. diff --git a/doc/src/aboutus.txt b/doc/src/aboutus.txt index e2364af013c0..db9c4dbba2d1 100644 --- a/doc/src/aboutus.txt +++ b/doc/src/aboutus.txt @@ -199,6 +199,7 @@ want to be mentioned here, so see our repository history for a full list). #. Matthias Toews: File permissions #. Jorge E. Cardona: Cleanup in polys #. Sanket Agarwal: Rewrite coverage_doctest.py script +#. Manoj Babu K.: Improve gamma function Up-to-date list in the order of the first contribution is given in the `AUTHORS `_ file. From 0899c5c9bcdbeef5fb45a96d17803f67917f1422 Mon Sep 17 00:00:00 2001 From: Sai Nikhil Date: Sat, 17 Mar 2012 01:28:08 +0530 Subject: [PATCH 082/104] Issue 3098: Heaviside Theta function for complex inputs Heaviside Theta function is defined only for real numbers. The output for non-real complex number inputs before my patch submission is as follows: >>> from sympy import Heaviside,I >>> Heaviside(2+3*I) Heaviside(2 + 3*I) Now I have raised a ValueError to fix this issue. --- sympy/functions/special/delta_functions.py | 3 +++ sympy/functions/special/tests/test_delta_functions.py | 6 ++++++ 2 files changed, 9 insertions(+) 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/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)') From 17b9f9ef065457a564f8c31cbbac8375167745fb Mon Sep 17 00:00:00 2001 From: Sean Vig Date: Sun, 18 Mar 2012 11:23:03 -0500 Subject: [PATCH 083/104] Add Sai Nikhil to the AUTHORS/aboutus. Welcome to SymPy! --- AUTHORS | 1 + doc/src/aboutus.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index a68bbe7d9d66..f12de84d5585 100644 --- a/AUTHORS +++ b/AUTHORS @@ -193,3 +193,4 @@ Matthias Toews Jorge E. Cardona Sanket Agarwal Manoj Babu K. +Sai Nikhil diff --git a/doc/src/aboutus.txt b/doc/src/aboutus.txt index db9c4dbba2d1..3991c953d773 100644 --- a/doc/src/aboutus.txt +++ b/doc/src/aboutus.txt @@ -200,6 +200,7 @@ want to be mentioned here, so see our repository history for a full list). #. 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. From 4ca68aaebdb49ffe41ef4c413c48ff08730fd749 Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Sun, 18 Mar 2012 11:01:59 -0700 Subject: [PATCH 084/104] cleaned docstrings for sets --- sympy/core/sets.py | 117 ++++++++++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 45 deletions(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index dacef16e19e0..e445608b7f29 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -11,9 +11,10 @@ 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 @@ -33,11 +34,11 @@ class Set(Basic): 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) @@ -45,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] @@ -108,7 +108,7 @@ def _complement(self): @property def inf(self): """ - The infimum of 'self'. + The infimum of 'self' >>> from sympy import Interval, Union @@ -126,7 +126,8 @@ def _inf(self): @property def sup(self): - """ The supremum of 'self'. + """ + The supremum of 'self' >>> from sympy import Interval, Union @@ -181,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 @@ -238,11 +239,10 @@ 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 ======== @@ -267,11 +267,15 @@ 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 + ========== - http://en.wikipedia.org/wiki/Cartesian_product + .. [1] http://en.wikipedia.org/wiki/Cartesian_product """ is_ProductSet = True @@ -294,7 +298,7 @@ def flatten(arg): def _contains(self, element): """ - in operator for ProductSets + 'in' operator for ProductSets >>> from sympy import Interval @@ -402,13 +406,17 @@ 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 + ========== - http://en.wikipedia.org/wiki/Interval_(mathematics) + .. [1] http://en.wikipedia.org/wiki/Interval_(mathematics) """ is_Interval = True @@ -442,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 @@ -458,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 @@ -637,7 +647,10 @@ class Union(Set): ======== Intersection - http://en.wikipedia.org/wiki/Union_(set_theory) + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Union_(set_theory) """ is_Union = True @@ -771,8 +784,7 @@ def _measure(self): return measure def as_relational(self, symbol): - """Rewrite a Union in terms of equalities and logic operators. - """ + """Rewrite a Union in terms of equalities and logic operators. """ return Or(*[set.as_relational(symbol) for set in self.args]) @property @@ -801,7 +813,10 @@ class Intersection(Set): ======== Union - http://en.wikipedia.org/wiki/Intersection_(set_theory) + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Intersection_(set_theory) """ is_Intersection = True @@ -1052,7 +1067,10 @@ class EmptySet(Set): ======== UniversalSet - http://en.wikipedia.org/wiki/Empty_set + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Empty_set """ __metaclass__ = Singleton is_EmptySet = True @@ -1103,7 +1121,10 @@ class UniversalSet(Set): ======== EmptySet - http://en.wikipedia.org/wiki/Universal_set + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Universal_set """ __metaclass__ = Singleton @@ -1143,7 +1164,10 @@ class FiniteSet(CountableSet): >>> 3 in FiniteSet(1, 2, 3, 4) True - http://en.wikipedia.org/wiki/Finite_set + References + ========== + + .. [1] http://en.wikipedia.org/wiki/Finite_set """ is_FiniteSet = True @@ -1184,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 @@ -1214,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 @@ -1244,8 +1270,7 @@ 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 return Or(*[Eq(symbol, elem) for elem in self]) @@ -1259,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): @@ -1294,8 +1322,7 @@ 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 return Or(*[Eq(symbol, elem) for elem in self]) From bc31870d317a63c4a85cf2231c6765d891f5d534 Mon Sep 17 00:00:00 2001 From: Matthew Rocklin Date: Sun, 18 Mar 2012 14:37:50 -0700 Subject: [PATCH 085/104] Dealt with python2.5 incompatibility --- sympy/core/sets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sympy/core/sets.py b/sympy/core/sets.py index e445608b7f29..af6efb6be9bf 100644 --- a/sympy/core/sets.py +++ b/sympy/core/sets.py @@ -930,7 +930,7 @@ def reduce(args): if len(args)==1: return args.pop() else: - return Intersection(*args, evaluate=False) + return Intersection(args, evaluate=False) class RealUnion(Union, RealSet): """ From 052f68185ef55481215fc104fa377c658e3b18bd Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sun, 18 Mar 2012 14:42:36 +0545 Subject: [PATCH 086/104] fix Add._eval_subs --- sympy/core/add.py | 2 +- sympy/core/tests/test_subs.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sympy/core/add.py b/sympy/core/add.py index c6fe6624dea2..1006fd3993e8 100644 --- a/sympy/core/add.py +++ b/sympy/core/add.py @@ -555,7 +555,7 @@ def _eval_subs(self, old, new): return Add(new, coeff_self, -coeff_old, *[s._subs(old, new) for s in ret_set]) - args_old = (-terms_old).args # (a+b+c+d).subs(-b-c,x) -> a-x+d + 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 diff --git a/sympy/core/tests/test_subs.py b/sympy/core/tests/test_subs.py index 5e2c5f00cae4..0460b65dedfd 100644 --- a/sympy/core/tests/test_subs.py +++ b/sympy/core/tests/test_subs.py @@ -344,6 +344,10 @@ def test_add(): # 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') From b25aa8342659b5eef25f47cba6fa196fbe6378e7 Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Mon, 19 Mar 2012 04:58:59 +0000 Subject: [PATCH 087/104] Make is_comparable an ordinary property. * Remove is_comparable from the old assumption system. * Remove hardcoded fallback to evalf() to evaluate assumptions is_positive, is_negative, etc. This behavior is now implemented in Expr._eval_is_positive and Expr._eval_is_negative. Classes that relied on the fallback must now inherit these methods or invoke them explicitly (see Add._eval_is_positive for the latter case). * This fixes the wrong result I.is_comparable == None * Fix atan2.eval(): only divide by x if x.is_zero is False --- sympy/core/add.py | 4 ++ sympy/core/assumptions.py | 40 +------------------- sympy/core/basic.py | 10 +++++ sympy/core/expr.py | 16 ++++++++ sympy/core/tests/test_assumptions.py | 42 ++++++++++----------- sympy/functions/elementary/trigonometric.py | 2 +- 6 files changed, 53 insertions(+), 61 deletions(-) diff --git a/sympy/core/add.py b/sympy/core/add.py index c6fe6624dea2..c45df78a7948 100644 --- a/sympy/core/add.py +++ b/sympy/core/add.py @@ -437,6 +437,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 +488,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] 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 d9acf2ed6966..22a1e1c48085 100644 --- a/sympy/core/basic.py +++ b/sympy/core/basic.py @@ -630,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): """ diff --git a/sympy/core/expr.py b/sympy/core/expr.py index a228f99d114c..97607fda323b 100644 --- a/sympy/core/expr.py +++ b/sympy/core/expr.py @@ -528,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: diff --git a/sympy/core/tests/test_assumptions.py b/sympy/core/tests/test_assumptions.py index 92741258e7b1..47edf14302ef 100644 --- a/sympy/core/tests/test_assumptions.py +++ b/sympy/core/tests/test_assumptions.py @@ -271,27 +271,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/functions/elementary/trigonometric.py b/sympy/functions/elementary/trigonometric.py index 784c94d8cf84..498e54c692b1 100644 --- a/sympy/functions/elementary/trigonometric.py +++ b/sympy/functions/elementary/trigonometric.py @@ -1403,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) From 6a56f5a499cdff365607b02f0c1dbcdb73eb8f45 Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Fri, 16 Mar 2012 02:27:11 +0000 Subject: [PATCH 088/104] Clean up sympy/functions/elementary/tests/test_complexes * Replace inner function tn() with module-level N_equals(), and remove unneeded indirection via test_numerically(). * Remove XFAIL for passing test for 2453. --- .../elementary/tests/test_complexes.py | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/sympy/functions/elementary/tests/test_complexes.py b/sympy/functions/elementary/tests/test_complexes.py index f3bf3e0ff1ab..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') @@ -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 From eb53485ff8ae6f2a12add6282f95f79d28fa0614 Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Sat, 30 Jul 2011 04:46:17 +0100 Subject: [PATCH 089/104] Remove now unused _eval_is_comparable methods. --- sympy/core/add.py | 1 - sympy/core/function.py | 12 ------------ sympy/core/mul.py | 1 - sympy/core/power.py | 7 ------- 4 files changed, 21 deletions(-) diff --git a/sympy/core/add.py b/sympy/core/add.py index c45df78a7948..70728c8c07d1 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)] diff --git a/sympy/core/function.py b/sympy/core/function.py index 8f86e5cb18d7..2d3edaca25ff 100644 --- a/sympy/core/function.py +++ b/sympy/core/function.py @@ -326,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 diff --git a/sympy/core/mul.py b/sympy/core/mul.py index 361b34d02aa5..1af3c4f4a045 100644 --- a/sympy/core/mul.py +++ b/sympy/core/mul.py @@ -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) diff --git a/sympy/core/power.py b/sympy/core/power.py index ebe9b15e9a86..22c168b0372e 100644 --- a/sympy/core/power.py +++ b/sympy/core/power.py @@ -121,13 +121,6 @@ def _eval_power(self, 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 - def _eval_is_even(self): if self.exp.is_integer and self.exp.is_positive: if self.base.is_even: From b656fa1a4c297da005e61e7a4f8cf9542c337dfb Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Sun, 31 Jul 2011 02:13:59 +0100 Subject: [PATCH 090/104] Remove uses of is_comparable in sympy/core/numbers.py --- sympy/core/numbers.py | 44 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py index cd327375b541..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 @@ -798,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))) @@ -811,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))) @@ -1157,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): @@ -1173,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): @@ -1190,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): @@ -1206,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): @@ -2218,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__ = [] @@ -2286,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__ = [] @@ -2323,7 +2320,6 @@ class NumberSymbol(AtomicExpr): __metaclass__ = Singleton is_commutative = True - is_comparable = True is_bounded = True is_finite = True is_number = True @@ -2375,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) @@ -2387,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 From 0313b004ffb6ea1e65b8924f4e64cc38946d016d Mon Sep 17 00:00:00 2001 From: Ronan Lamy Date: Wed, 7 Sep 2011 20:18:35 +0100 Subject: [PATCH 091/104] Add tests for is_number --- sympy/core/tests/test_assumptions.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sympy/core/tests/test_assumptions.py b/sympy/core/tests/test_assumptions.py index 47edf14302ef..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) From 4dc19931139cc1d418749256afb18dbe407a2839 Mon Sep 17 00:00:00 2001 From: "Alexey U. Gudchenko" Date: Mon, 26 Dec 2011 22:47:43 +0400 Subject: [PATCH 092/104] Added tutorial Russian translation of the new format: tutorial.ru.po --- doc/src/tutorial.ru.po | 566 +++++++++++++++++++++-------------------- 1 file changed, 288 insertions(+), 278 deletions(-) diff --git a/doc/src/tutorial.ru.po b/doc/src/tutorial.ru.po index bb2d8f1c3742..7f4a1d65cc98 100644 --- a/doc/src/tutorial.ru.po +++ b/doc/src/tutorial.ru.po @@ -3,483 +3,493 @@ # This file is distributed under the same license as the SymPy package. # FIRST AUTHOR , YEAR. # -#, 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: 2011-12-17 15:29\n" +"PO-Revision-Date: 2011-12-26 13:45+0530\n" +"Last-Translator: \n" "Language-Team: LANGUAGE \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." -msgstr "" +msgstr "SymPy - это открытая библиотека для символьных вычислений на языке Python. Ее цель - стать полнофункциональной системой компьютерной алгебры (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 "" +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:" -msgstr "" +msgstr "Самый простой способ скачать SymPy - пройти на http://code.google.com/p/sympy/ и загрузить .tar файл с последней версией из блока Featured Downloads:" +# 65bde5e4aded4818bc11c4b9cfc8a3b7 #: ../../src/tutorial.txt:34 -# 95da13d57e534846abe6d3ae2b3f39d0 msgid "Unpack it:" -msgstr "" +msgstr "Распаковать его:" +# 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 "" +msgstr "Если вы используете SymPy в вашей программе, рекомендуется использовать его как показано выше. Вы можете установить Sympy так же как и любой другой Python модуль, используя ./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." -msgstr "" +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, вы можете использовать специальную оболочку IPython под названием ``isympy`` (она расположена в ``bin/isympy``, относительно директории с исходным кодом),которая является стандартным терминалом Python, но с уже импортированными важными модулями 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." -msgstr "" +msgstr "Команды, введенные вами, обозначены жирным шрифтом. То, что мы делали тремя строчками в стандартном терминале Python, мы можем сделать одной строчкой в isympy." +# f53c41b84d8b4d47894877572350325c #: ../../src/tutorial.txt:124 -# a50006bf0aed4e2a8a3967edb9ef7afa msgid "Using SymPy as a calculator" -msgstr "" +msgstr "Использование SymPy в качестве калькулятора" +# c6a7837a22d44c27a5d35a905779bd84 #: ../../src/tutorial.txt:126 # 88460153b0404c1c80d7d9fef1b9d530 msgid "SymPy has three built-in numeric types: Float, Rational and Integer." -msgstr "" +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 "" +msgstr "Rational реализует обыкновенную дробь с помощью двух целых чисел: числителя и знаменателя. Так, Rational(1,2) является числом 1/2, Rational(5,2) является 5/2, и так далее." -#: ../../src/tutorial.txt:146 -# e2a928259dea44eaa3cabe0fee4bdd78 +# 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 "" +msgstr "В Python-интерпретаторе при делении двух питоновских чисел типа int с помощью оператора \"/\" получается или питоновское int (в старых версиях, без использования ``from __future__ import division``) или питоновский тип float (стандарт \"true division\" в Python 3). Этот же стандарт \"true division\" по умолчанию включен и в isympy::" -#: ../../src/tutorial.txt:156 -# 3f86f4cd341e4872b08b2ae231c0c8c8 +# 723ba484cf134b71aaf8e7140c9a3954 +#: ../../src/tutorial.txt:157 msgid "But in earlier Python versions where division has not been imported, a truncated int will result::" -msgstr "" +msgstr "В более ранних версиях Python этого не произойдет, и результатом будет целочисленное деление::" -#: ../../src/tutorial.txt:162 -# 9dbc717a5cd64ee7977c80343f7a92d9 +# 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 "" +msgstr "В обоих случаях вы имеете дело не с объектом Number, который представляет число в SymPy, а с питоновскими числами, которые создаются по умолчанию. Скорее всего, в большинстве случаев вы будете работать с дробными числами, поэтому для того чтобы получать результат в виде объектов SymPy убедитесь, что вы используете класс Rational. Кому-то может показаться удобным обозначать Rational как ``R``::" -#: ../../src/tutorial.txt:174 -# 3ee8ed81cd7e4bfdadf807a0b9b12e2d +# 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 "" +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 "" +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 "" +msgstr "Символ ``oo`` используется для работы с математической бесконечностью::" -#: ../../src/tutorial.txt:197 -# aa2936b52b8042dead3f4bb1c049edc1 +# addc7fddfd294a0e8da003463f35b614 +#: ../../src/tutorial.txt:200 msgid "Symbols" -msgstr "" +msgstr "Переменные" -#: ../../src/tutorial.txt:199 -# a2cdfc56d552408aacd4e540068fba20 +# 8508a3480ca34bd4a8741733e17ccc56 +#: ../../src/tutorial.txt:202 msgid "In contrast to other Computer Algebra Systems, in SymPy you have to declare symbolic variables explicitly::" -msgstr "" +msgstr "В отличие от многих других систем компьютерной алгебры, в SymPy вам нужно явно декларировать символьные переменные::" + +# 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: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::" +# 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 "" -#: ../../src/tutorial.txt:220 -# 76d8e041db1c4be7926bb34608f07db0 +# aa93d9743638443ebbae8bc5bee19e56 +#: ../../src/tutorial.txt:227 +msgid "Instances of the Symbol class \"play well together\" and are the building blocks of expresions::" +msgstr "Экземпляры класса Symbol взаимодействуют друг с другом и составляют алгебраические выражения::" + +# c613786cee894688b20cdccfeaac8c9b +#: ../../src/tutorial.txt:239 msgid "They can be substituted with other numbers, symbols or expressions using ``subs(old, new)``::" -msgstr "" +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 "" +msgstr "Далее, на всем протяжении теста, мы будем предполагать, что для всех примеров мы запустили команду::" -#: ../../src/tutorial.txt:235 -# 94b13605b8b74a32a7780b10b7c94cfa +# 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 "" +msgstr "Это придаст более качественное отображение выражений при печати. Смотрите раздел :ref:`printing-tutorial-ru` ниже. Если же у вас установлен шрифт с юникодом, вы можете использовать опцию use_unicode=True для еще более красивого вывода данных. Подробнее о системе печати смотрите раздел :ref:`printing-tutorial-ru` ниже." -#: ../../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 "" +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 "" +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)``::" -msgstr "" +# 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 +# d67693fb8cc04954be3bff6aaa712bfa +#: ../../src/tutorial.txt:336 msgid "for some non-trivial examples on limits, you can read the test file `test_demidovich.py `_" -msgstr "" +msgstr "Для более сложных примеров вычисления пределов, вы можете ознакомиться с тестовым файлом `test_demidovich.py `_ " -#: ../../src/tutorial.txt:319 -# 839cdd6f6dc246d381cf4ae32fe75973 +# c92009d7b6df4e2d8f9fe9ad14657bbd +#: ../../src/tutorial.txt:343 msgid "Differentiation" -msgstr "" +msgstr "Дифференцирование" -#: ../../src/tutorial.txt:321 -# 8e81b82b9f6f44e1ab31a0eecf761366 +# 476aa0220145495a932178c9fc3b305a +#: ../../src/tutorial.txt:345 msgid "You can differentiate any SymPy expression using ``diff(func, var)``. Examples::" -msgstr "" +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 "" +msgstr "Правильность производной вы можете проверить следующим способом::" -#: ../../src/tutorial.txt:340 -# cae093b9d4874e55af4aa3e86c9333a8 +# 5ffab586313e4970aa3c933d631bc676 +#: ../../src/tutorial.txt:366 msgid "Higher derivatives can be calculated using the ``diff(func, var, n)`` method::" -msgstr "" +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 "" +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 +# 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 "" +msgstr "SymPy поддерживает вычисление определенных и неопределенных интегралов с помощью функции ``integrate()``. Она использует расширенный алгоритм Risch-Norman, с некоторыми шаблонами и эвристиками. Можно вычислять интегралы трансцендентных, простых и специальных функций::" -#: ../../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 "" -#: ../../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." -msgstr "" +# 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 +# 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 "" +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 +# 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 "" +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 "" +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:" -msgstr "" +# 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.:" -msgstr "" +# 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 +# dcbf982351c1414bb0c2653039d6f569 +#: ../../src/tutorial.txt:803 msgid "See also the wiki `Pretty Printing `_ for more examples of a nice unicode printing." -msgstr "" +msgstr "Также вы можете прочитать статью `Pretty Printing `_ из нашего Вики для изучения примеров Pretty-print с юникодом." -#: ../../src/tutorial.txt:776 -# 919e23979e684267a33d1f30399957ed -msgid "Tip: To make the pretty printing default in the python interpreter, use::" -msgstr "" +# 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 +# 51219a05b9a84e79b754d8d03b7fc4fb +#: ../../src/tutorial.txt:889 msgid "``isympy`` calls ``pprint`` automatically, so that's why you see pretty printing by default." -msgstr "" +msgstr "``isympy`` вызывает ``pprint`` автоматически, поэтому Pretty-print будет использована по умолчанию." -#: ../../src/tutorial.txt:861 -# a55e545ba25141af9f6bd2df4b35f02c +# 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 "" +msgstr "Также доступен модуль печати - ``sympy.printing``. Через этот модуль доступны следующий функции печати:" -#: ../../src/tutorial.txt:864 -# e5329a0202044246a97cad451de550a0 +# 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 "" +msgstr "``pretty(expr)``, ``pretty_print(expr)``, ``pprint(expr)``: Возвращает или выводит на экран, соответственно, Красивое представление выражения ``expr``. Это то же самое, что и второй уровень представления, описанный выше." -#: ../../src/tutorial.txt:866 -# 0bac12dbda6540028b304f4b655ae8e3 +# ee0ca8e34ce04b458228cb9d6565ec1a +#: ../../src/tutorial.txt:897 msgid "``latex(expr)``, ``print_latex(expr)``: Return or print, respectively, a `LaTeX `_ representation of ``expr``" -msgstr "" +msgstr "``latex(expr)``, ``print_latex(expr)``: Возвращает или выводит на экран, соответственно, `LaTeX `_ -представление ``expr``" -#: ../../src/tutorial.txt:868 -# 7ec29518a7074721824c874431d421b8 +# ebfeade491644b0b85f72b00bd3c1bdf +#: ../../src/tutorial.txt:899 msgid "``mathml(expr)``, ``print_mathml(expr)``: Return or print, respectively, a `MathML `_ representation of ``expr``." -msgstr "" +msgstr "``mathml(expr)``, ``print_mathml(expr)``: Возвращает или выводит на экран, соответственно, `MathML `_ -представление ``expr``." -#: ../../src/tutorial.txt:870 -# db586a15ee234c81a4fc8c2142a7f669 +# 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 "" +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 +# 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 "" +msgstr "Теперь самое время узнать больше о SymPy. Прочитайте :ref:`Руководство пользователя SymPy ` и :ref:`Описание модулей SymPy `." -#: ../../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." -msgstr "" +# 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 `_, сайт, который содержит множество полезных примеров, руководств и советов, созданных нами и нашим сообществом. Мы будем рады, если и вы внесете в него свой вклад." From 9ea4968a6e90fc4393d6ec3ec87961afff93dcd3 Mon Sep 17 00:00:00 2001 From: "Alexey U. Gudchenko" Date: Mon, 26 Dec 2011 22:49:21 +0400 Subject: [PATCH 093/104] Remove old tutorial Russian translation: tutorial.ru.txt --- doc/src/tutorial.ru.txt | 921 ---------------------------------------- 1 file changed, 921 deletions(-) delete mode 100644 doc/src/tutorial.ru.txt 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 `_, -сайт, который содержит множество полезных примеров, руководств и советов, -созданных нами и нашим сообществом. -Мы будем рады, если и вы внесете в него свой вклад. From ff2501fd6fda63a4e7bc33882a1d128e6115756c Mon Sep 17 00:00:00 2001 From: "Alexey U. Gudchenko" Date: Mon, 26 Dec 2011 22:51:59 +0400 Subject: [PATCH 094/104] Updated doc/Makefile: added 'htmli18n' in help string --- doc/Makefile | 1 + 1 file changed, 1 insertion(+) 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 From 6b2d075aac68d0d9c38efe371068dd3314b7e650 Mon Sep 17 00:00:00 2001 From: "Alexey U. Gudchenko" Date: Tue, 27 Dec 2011 00:42:25 +0400 Subject: [PATCH 095/104] Update russian translation of tutorial. --- doc/src/tutorial.pot | 200 +++++++++--------- doc/src/tutorial.ru.po | 446 ++++++++++++++++++++++++++++++++--------- 2 files changed, 448 insertions(+), 198 deletions(-) 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 7f4a1d65cc98..83125a67c322 100644 --- a/doc/src/tutorial.ru.po +++ b/doc/src/tutorial.ru.po @@ -1,16 +1,17 @@ # 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 # msgid "" msgstr "" "Project-Id-Version: SymPy 0.7.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-12-17 15:29\n" -"PO-Revision-Date: 2011-12-26 13:45+0530\n" -"Last-Translator: \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" @@ -27,13 +28,31 @@ msgstr "Введение" # d007423f3c5d4407b0ca3221168f4a1e #: ../../src/tutorial.txt:12 -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. Ее цель - стать полнофункциональной системой компьютерной алгебры (CAS), сохраняя код максимально простым, понятным и легко расширяемым. SymPy полностью написан на Python и не требует сторонних библиотек." +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 -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 `. или сразу `исходный код `_ ." +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 @@ -42,13 +61,19 @@ msgstr "Первые шаги с SymPy" # 6f67d8997f1a474288d6bd50f5ddbc4f #: ../../src/tutorial.txt:28 -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/ и загрузить .tar файл с последней версией из блока 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 msgid "Unpack it:" -msgstr "Распаковать его:" +msgstr "" +"Для Windows-систем, нужно скачать и запустить установочный .exe файл. В " +"POSIX-совместимых системах нужно скачать .tar.gz файл и распаковать его:" # 76f6f7d72759408084134e093b5d9aea #: ../../src/tutorial.txt:40 @@ -57,13 +82,26 @@ msgstr "и запустить из интерпретатора Python:" # 4ddd39aeea714e9f8495cd77f7e4d7dd #: ../../src/tutorial.txt:54 -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 в вашей программе, рекомендуется использовать его как показано выше. Вы можете установить Sympy так же как и любой другой Python модуль, используя ./setup.py install. Или, если вы работаете в Linux, можно установить пакет ``python-sympy`` с помощью системы установки программ:" +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 -msgid "For other means how to install SymPy, consult the Downloads_ tab on the SymPy's webpage." -msgstr "Для других вариантов установки SymPy, посмотрите, пожалуйста, вкладку Downloads_ на домашней странице SymPy." +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 @@ -72,13 +110,29 @@ msgstr "Консоль isympy" # 342865b9196045a6a2a11ae9d69bc980 #: ../../src/tutorial.txt:89 -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, вы можете использовать специальную оболочку IPython под названием ``isympy`` (она расположена в ``bin/isympy``, относительно директории с исходным кодом),которая является стандартным терминалом Python, но с уже импортированными важными модулями SymPy, определенными переменными x, y, z и некоторыми другими надстройками:" +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 -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." +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 @@ -86,45 +140,89 @@ msgid "Using SymPy as a calculator" msgstr "Использование SymPy в качестве калькулятора" # c6a7837a22d44c27a5d35a905779bd84 -#: ../../src/tutorial.txt:126 # 88460153b0404c1c80d7d9fef1b9d530 +#: ../../src/tutorial.txt:126 msgid "SymPy has three built-in numeric types: Float, Rational and Integer." -msgstr "SymPy поддерживает три типа численных данных: Float, Rational и Integer." +msgstr "" +"SymPy поддерживает три типа численных данных: Float, Rational и Integer." # 68eba98bb6f742d799aba6051f7f3d0f #: ../../src/tutorial.txt:128 -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, и так далее." +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, и так далее." # 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 с помощью оператора \"/\" получается или питоновское int (в старых версиях, без использования ``from __future__ import division``) или питоновский тип float (стандарт \"true division\" в Python 3). Этот же стандарт \"true division\" по умолчанию включен и в isympy::" +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 этого не произойдет, и результатом будет целочисленное деление::" +msgid "" +"But in earlier Python versions where division has not been imported, a " +"truncated int will result::" +msgstr "" +"В более ранних версиях Python этого не получится, и результатом будет " +"целочисленное деление::" # 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 убедитесь, что вы используете класс Rational. Кому-то может показаться удобным обозначать Rational как ``R``::" +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), и заданы с некоторой точностью::" +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)::" # 366cb3315e5f4371927639cebbf8a726 #: ../../src/tutorial.txt:189 msgid "as you see, evalf evaluates the expression to a floating-point number" -msgstr "как вы видите, функция evalf переводит выражение в число с плавающей точкой" +msgstr "" +"как вы видите, функция evalf переводит исходное выражение в число с " +"плавающей точкой. Вычисления можно проводить с большей точностью. Для этого " +"нужно передать в качестве аргумента этой функции требуемое число десятичных " +"знаков." # b300cf55ed11465bac6e0991d6f61a15 #: ../../src/tutorial.txt:191 msgid "The symbol ``oo`` is used for a class defining mathematical infinity::" -msgstr "Символ ``oo`` используется для работы с математической бесконечностью::" +msgstr "" +"Для работы с математической бесконечностью используется символ ``oo``::" # addc7fddfd294a0e8da003463f35b614 #: ../../src/tutorial.txt:200 @@ -133,38 +231,71 @@ msgstr "Переменные" # 8508a3480ca34bd4a8741733e17ccc56 #: ../../src/tutorial.txt:202 -msgid "In contrast to other Computer Algebra Systems, in SymPy you have to declare symbolic variables explicitly::" -msgstr "В отличие от многих других систем компьютерной алгебры, в SymPy вам нужно явно декларировать символьные переменные::" +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." +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." # 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:" +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`` добавляет созданные переменные в текущее пространство имен:" # aa93d9743638443ebbae8bc5bee19e56 #: ../../src/tutorial.txt:227 -msgid "Instances of the Symbol class \"play well together\" and are the building blocks of expresions::" -msgstr "Экземпляры класса Symbol взаимодействуют друг с другом и составляют алгебраические выражения::" +msgid "" +"Instances of the Symbol class \"play well together\" and are the building " +"blocks of expresions::" +msgstr "" +"Экземпляры класса Symbol взаимодействуют друг с другом. Таким образом, с " +"помощью них конструируются алгебраические выражения::" # c613786cee894688b20cdccfeaac8c9b #: ../../src/tutorial.txt:239 -msgid "They can be substituted with other numbers, symbols or expressions using ``subs(old, new)``::" -msgstr "Переменные могут быть заменены на другие числа, переменные или выражения функцией ``subs(old, new)``::" +msgid "" +"They can be substituted with other numbers, symbols or expressions using " +"``subs(old, new)``::" +msgstr "" +"Переменные могут быть заменены на другие переменные, числа или выражения с " +"помощью функции подстановки ``subs(old, new)``::" # 3872add29a8b4a71a5e26a3218130491 #: ../../src/tutorial.txt:250 msgid "For the remainder of the tutorial, we assume that we have run::" -msgstr "Далее, на всем протяжении теста, мы будем предполагать, что для всех примеров мы запустили команду::" +msgstr "" +"Теперь, с этого момента, для всех написанных ниже примеров мы будем " +"предполагать, что запустили следующую команду по настройке системы " +"отображения результатов::" # 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 для еще более красивого вывода данных. Подробнее о системе печати смотрите раздел :ref:`printing-tutorial-ru` ниже." +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 для еще более красивого вывода." # 17ee30ecb05a410d94cccf0cc229ae5b #: ../../src/tutorial.txt:260 @@ -174,12 +305,16 @@ msgstr "Алгебра" # 673fbe9f608748128027755ac1b0d7cb #: ../../src/tutorial.txt:262 msgid "For partial fraction decomposition, use ``apart(expr, x)``::" -msgstr "Чтобы разложить выражение на простейшие дроби, используйте ``apart(expr, x)``::" +msgstr "" +"Чтобы разложить выражение на простейшие дроби используется функция ``apart" +"(expr, x)``::" # 723c57a6e36f43eca18771e2ad246955 #: ../../src/tutorial.txt:287 msgid "To combine things back together, use ``together(expr, x)``::" -msgstr "Чтобы снова привести дроби к общему знаменателю, используйте ``together(expr, x)``::" +msgstr "" +"Чтобы снова привести дробь к общему знаменателю используется функция " +"``together(expr, x)``::" # 607fa824a12d461d9eaf6c83bbc902d0 #: ../../src/tutorial.txt:309 @@ -193,18 +328,30 @@ msgstr "Пределы" # 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)``::" +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)``::" # 7cc96e845d0a4c479199f4a51ed7d665 #: ../../src/tutorial.txt:325 msgid "you can also calculate the limit at infinity::" -msgstr "также вы можете вычислить предел при x, стремящемся к бесконечности::" +msgstr "также вы можете вычислять пределы при x, стремящемся к бесконечности::" # 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 `_ " +msgid "" +"for some non-trivial examples on limits, you can read the test file " +"`test_demidovich.py `_" +msgstr "" +"Для более сложных примеров вычисления пределов, вы можете обратится к файлу " +"с тестами `test_demidovich.py `_ " # c92009d7b6df4e2d8f9fe9ad14657bbd #: ../../src/tutorial.txt:343 @@ -213,18 +360,27 @@ msgstr "Дифференцирование" # 476aa0220145495a932178c9fc3b305a #: ../../src/tutorial.txt:345 -msgid "You can differentiate any SymPy expression using ``diff(func, var)``. Examples::" -msgstr "Вы можете продифференцировать любое выражение SymPy, используя ``diff(func, var)``. Примеры::" +msgid "" +"You can differentiate any SymPy expression using ``diff(func, var)``. " +"Examples::" +msgstr "" +"Продифференцировать любое выражение SymPy, можно используя ``diff(func, var)" +"``. Примеры::" # 35850640eccf4071b1b0318a49055f39 #: ../../src/tutorial.txt:358 msgid "You can check, that it is correct by::" -msgstr "Правильность производной вы можете проверить следующим способом::" +msgstr "" +"Можно, кстати, через пределы проверить правильность вычислений производной::" # 5ffab586313e4970aa3c933d631bc676 #: ../../src/tutorial.txt:366 -msgid "Higher derivatives can be calculated using the ``diff(func, var, n)`` method::" -msgstr "Производные более высших порядков вы можете вычислять, используя функцию ``diff(func, var, n)``::" +msgid "" +"Higher derivatives can be calculated using the ``diff(func, var, n)`` " +"method::" +msgstr "" +"Производные более высших порядков можно вычислить, используя дополнительный " +"параметр этой же функции ``diff(func, var, n)``::" # 0624a0af400f4e78a351b056a7ba009c #: ../../src/tutorial.txt:383 @@ -234,7 +390,8 @@ msgstr "Разложение в ряд" # b40c746b225d4749a9298ad077c3ce4d #: ../../src/tutorial.txt:385 msgid "Use ``.series(var, point, order)``::" -msgstr "Используйте ``.series(var, point, order)``::" +msgstr "" +"Для разложения в ряд используйте метод ``.series(var, point, order)``::" # f9106b494ab2465bbbd4108736f67a1e #: ../../src/tutorial.txt:400 @@ -248,8 +405,16 @@ msgstr "Интегрирование" # 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()``. Она использует расширенный алгоритм Risch-Norman, с некоторыми шаблонами и эвристиками. Можно вычислять интегралы трансцендентных, простых и специальных функций::" +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()``. Она использует расширенный алгоритм Риша-" +"Нормана и некоторые шаблоны и эвристики. Можно вычислять интегралы " +"трансцендентных, простых и специальных функций::" # 6ff3ff4c189f45078f75a93aa2863ef9 #: ../../src/tutorial.txt:430 @@ -278,8 +443,14 @@ 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::" +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 и т.д), которые " +"определяют поведение этих символов при вычислении символьных выражений::" # d726887f4f0444dba6e4ef6a2bb702d6 #: ../../src/tutorial.txt:491 @@ -318,8 +489,7 @@ msgstr "Дифференциальные уравнения" # 18467aefa0364cec936571494c1993da # 65279c1f5c4a448fbb813790d956ac48 -#: ../../src/tutorial.txt:652 -#: ../../src/tutorial.txt:672 +#: ../../src/tutorial.txt:652 ../../src/tutorial.txt:672 msgid "In ``isympy``::" msgstr "В ``isympy``::" @@ -331,7 +501,7 @@ msgstr "Алгебраические уравнения" # fd5aedc6ba9f45b4868c10764f9e6797 #: ../../src/tutorial.txt:685 msgid "Linear Algebra" -msgstr "Линейная Алгебра" +msgstr "Линейная алгебра" # 372cf49866874ec1a97d6cb813f8b63b #: ../../src/tutorial.txt:690 @@ -346,12 +516,14 @@ msgstr "Матрицы задаются с помощью конструктор # 0289f9cec4f140c6adc90aa38f84efee #: ../../src/tutorial.txt:700 msgid "They can also contain symbols::" -msgstr "Также вы можете использовать в матрице символьные переменные::" +msgstr "В матрицах вы также можете использовать символьные переменные::" # ced23cc091a34db6be3b5597fd056f32 #: ../../src/tutorial.txt:715 msgid "For more about Matrices, see the Linear Algebra tutorial." -msgstr "Для того, чтобы получить больше информации, прочитайте, пожалуйста, Руководство по Линейной Алгебре." +msgstr "" +"Для того, чтобы узнать о матрицах подробнее, прочитайте, пожалуйста, " +"Руководство по Линейной Алгебре." # 71124cfe123649c28c7a61251d0b1cdd #: ../../src/tutorial.txt:720 @@ -360,18 +532,28 @@ msgstr "Сопоставление с образцом" # 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``. Функция вернет словарь с необходимыми заменами, например::" +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``. Эта функция вернет словарь с " +"необходимыми заменами, например::" # 06d4a8e419854045af79b0076e71616a #: ../../src/tutorial.txt:736 msgid "If the match is unsuccessful, it returns ``None``::" -msgstr "Если сопоставление не удалось, функция вернет``None``::" +msgstr "Если же сопоставление не удалось, функция вернет``None``::" # 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 для исключения некоторых значений из результата::" +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 для исключения некоторых значений " +"из результата::" # a692f291013a4777a4771188901c6bf2 #: ../../src/tutorial.txt:755 @@ -381,7 +563,7 @@ msgstr "Печать" # 8cfe16423a79408c828d270def412899 #: ../../src/tutorial.txt:757 msgid "There are many ways to print expressions." -msgstr "Существует несколько способов для того, чтобы вывести на экран выражения." +msgstr "Реализовано несколько способов вывода выражений на экран." # a9891e6028b644879ce5e3a8489a72a5 #: ../../src/tutorial.txt:759 @@ -391,7 +573,9 @@ msgstr "**Стандартный**" # 9802b10f6c2d4b7997302f4315e8bf2a #: ../../src/tutorial.txt:761 msgid "This is what ``str(expression)`` returns and it looks like this:" -msgstr "Стандартный способ представлен фукнцией ``str(expression)`` и работает следующиим образом:" +msgstr "" +"Стандартный способ представлен функцией ``str(expression)``, которая " +"работает следующим образом:" # 461df293b09c46b1bf24bc5c335b4401 #: ../../src/tutorial.txt:772 @@ -401,22 +585,37 @@ msgstr "**Красивая печать**" # 29c9e2204a0948db9d8328300f3bdd1a #: ../../src/tutorial.txt:774 msgid "Nice ascii-art printing is produced by the ``pprint`` function:" -msgstr "Это способ печати выражений, основанный на ascii-графике и реализованный в функции ``pprint``:" +msgstr "" +"Этот способ печати выражений основан на ascii-графике и реализован через " +"функцию ``pprint``:" # 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``:" +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``:" # dcbf982351c1414bb0c2653039d6f569 #: ../../src/tutorial.txt:803 -msgid "See also the wiki `Pretty Printing `_ for more examples of a nice unicode printing." -msgstr "Также вы можете прочитать статью `Pretty Printing `_ из нашего Вики для изучения примеров Pretty-print с юникодом." +msgid "" +"See also the wiki `Pretty Printing `_ for more examples of a nice unicode printing." +msgstr "" +"Для изучения подробных примеров работы Pretty-print с юникодом вы можете " +"обратится к статье `Pretty Printing `_ из нашего Вики." # af26680251b0437e807a48d7946c3724 #: ../../src/tutorial.txt:807 -msgid "Tip: To make pretty printing the default in the Python interpreter, use::" -msgstr "Совет: Чтобы активировать Pretty-print по умолчанию в интерпретаторе Python, используйте::" +msgid "" +"Tip: To make pretty printing the default in the Python interpreter, use::" +msgstr "" +"Совет: Чтобы активировать Pretty-print по умолчанию в интерпретаторе Python, " +"используйте::" # b34cf35aca67417bbedfd0f9912d3dab #: ../../src/tutorial.txt:830 @@ -440,7 +639,9 @@ msgstr "**Pyglet**" # 8ea61d943b2b4960a879932eba46bc1b #: ../../src/tutorial.txt:881 -msgid "If pyglet is installed, a pyglet window will open containing the LaTeX rendered expression:" +msgid "" +"If pyglet is installed, a pyglet window will open containing the LaTeX " +"rendered expression:" msgstr "Появится окно pyglet с отрисованным выражением LaTeX:" # 8741a66d24b04738a563afc90a41567b @@ -450,33 +651,62 @@ msgstr "Примечания" # 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 будет использована по умолчанию." +msgid "" +"``isympy`` calls ``pprint`` automatically, so that's why you see pretty " +"printing by default." +msgstr "" +"``isympy`` вызывает ``pprint`` автоматически, по этой причине Pretty-print " +"будет включен в ``isympy`` по умолчанию." # 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``. Через этот модуль доступны следующий функции печати:" +msgid "" +"Note that there is also a printing module available, ``sympy.printing``. " +"Other printing methods available through this module are:" +msgstr "" +"Также доступен модуль печати - ``sympy.printing``. Через этот модуль " +"доступны следующий функции печати:" # 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``. Это то же самое, что и второй уровень представления, описанный выше." +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``. " # 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``" +msgid "" +"``latex(expr)``, ``print_latex(expr)``: Return or print, respectively, a " +"`LaTeX `_ representation of ``expr``" +msgstr "" +"``latex(expr)``, ``print_latex(expr)``: Возвращает или выводит на экран, " +"соответственно, `LaTeX `_ -представление " +"``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``." +msgid "" +"``mathml(expr)``, ``print_mathml(expr)``: Return or print, respectively, a " +"`MathML `_ representation of ``expr``." +msgstr "" +"``mathml(expr)``, ``print_mathml(expr)``: Возвращает или выводит на экран, " +"соответственно, `MathML `_ -представление ``expr``." # 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 `_ ." +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 `_ ." # e76c7af419504e458893b76b1b485fd4 #: ../../src/tutorial.txt:904 @@ -485,11 +715,31 @@ msgstr "Другие справочники" # 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 `." +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 `_, сайт, который содержит множество полезных примеров, руководств и советов, созданных нами и нашим сообществом. Мы будем рады, если и вы внесете в него свой вклад." - +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 "Этот текст доступен на других языках:" From e31cea5a6cb188be29e7103585df6f0bb1a7acc2 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Tue, 20 Mar 2012 07:50:17 +0545 Subject: [PATCH 096/104] add sort=True keyword to __print_Boolean --- sympy/printing/pretty/pretty.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sympy/printing/pretty/pretty.py b/sympy/printing/pretty/pretty.py index 19a1de0b0659..661ea4106f55 100644 --- a/sympy/printing/pretty/pretty.py +++ b/sympy/printing/pretty/pretty.py @@ -132,11 +132,10 @@ def _print_Not(self, e): else: return self._print_Function(e) - def __print_Boolean(self, e, char): - if isinstance(e, Basic): + def __print_Boolean(self, e, char, sort=True): + args = e.args + if sort: args = sorted(e.args, key=default_sort_key) - else: - args = e arg = args[0] pform = self._print(arg) @@ -186,8 +185,7 @@ def _print_Nor(self, e): def _print_Implies(self, e): if self._use_unicode: - # don't sort implies' args - return self.__print_Boolean(e.args, u"\u2192") + return self.__print_Boolean(e, u"\u2192", sort=False) else: return self._print_Function(e) From f407c2c51756b86f6647d889f30dd5048ab20fa0 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sun, 18 Mar 2012 16:11:19 +0545 Subject: [PATCH 097/104] 3164: cse watches for production of non-Basic --- sympy/simplify/cse_main.py | 2 ++ sympy/simplify/cse_opts.py | 9 +++++---- sympy/simplify/tests/test_cse.py | 6 +++++- 3 files changed, 12 insertions(+), 5 deletions(-) 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/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]) From ce03c7d85326753e0287c78e9a373564a1206c0b Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Tue, 20 Mar 2012 14:57:23 +0545 Subject: [PATCH 098/104] watch for exp when rejoining bkey in powsimp --- sympy/simplify/simplify.py | 6 +++--- sympy/simplify/tests/test_simplify.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sympy/simplify/simplify.py b/sympy/simplify/simplify.py index 59c522fee24c..0815babb8c42 100644 --- a/sympy/simplify/simplify.py +++ b/sympy/simplify/simplify.py @@ -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_simplify.py b/sympy/simplify/tests/test_simplify.py index d06b6ead1011..ce6d8d2a23bc 100644 --- a/sympy/simplify/tests/test_simplify.py +++ b/sympy/simplify/tests/test_simplify.py @@ -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') From 2c7c80d9fe8af29b12eaf2e20dde54801f4f1094 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Mon, 5 Mar 2012 23:43:32 +0545 Subject: [PATCH 099/104] 3109: Pow._eval_power rules more accurate The rules for combining (x**a)**b into x**(a*b) have been updated; the conditions are enumerated in Pow._eval_power. The same sort of thing should also be done to exp (see issue 3109). Here are comments about the old rules: Formerly- The following was ok: - if other.is_integer: - return Pow(b, e * other) The condition that other.is_real should not be added; if e is complex then the joined exponent may not be valid, e.g. (3**(4*I))**(1/2) != 3**(2*I) if b.is_nonnegative and (e.is_real or other.is_real): - return Pow(b, e * other) ok: - if e.is_even and b.is_real: # hence b is pos and e is real - return Pow(abs(b), e * other) only valid if e.is_real and then it's true whether other is real or not - if abs(e) < S.One and other.is_real: - return Pow(b, e * other) ok (if that's how exp_polar should behave) - if b.is_polar: - return Pow(b, e * other) The rules above are missing the handling of possible complex powers where e.is_real is False and abs(e) < S.Pi/abs(log(b)) and the simple case where other is a multiple of 1/2 and abs(e) < S.Pi/abs(log(b)) This function can be used to test whether two exponents should combine. if False is printed, then (e**a)**b should not become e**(a*b) and the difference between (e**a)**b and e**(a*b) is printed. >>> def foo(a,b,e=E): ... ib=S(1)/b;print abs(a*log(e)/pi).n() < 1 ... return ((e**a).n()**ib - e**(a*ib)).n(chop=1e-10) ... >>> foo(2*I,3,6*I) False 0.0892124078593689 - 0.601228142633338*I >>> foo(I/3,3,6*I) True 0 --- sympy/core/power.py | 28 +++++++++++++++------------- sympy/core/tests/test_eval_power.py | 21 +++++++++++++++++++-- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/sympy/core/power.py b/sympy/core/power.py index 22c168b0372e..1f8b270948c2 100644 --- a/sympy/core/power.py +++ b/sympy/core/power.py @@ -106,20 +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) + 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: diff --git a/sympy/core/tests/test_eval_power.py b/sympy/core/tests/test_eval_power.py index 5c016bec9517..eb1277dbe4fc 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) @@ -188,3 +190,18 @@ 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) + +@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) From 6b40d7d2593e801480829271600f0d164fc08409 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Wed, 7 Mar 2012 08:48:23 +0545 Subject: [PATCH 100/104] 3109: exp._eval_power rules more accurate --- sympy/functions/elementary/exponential.py | 34 ++++++++++++++++--- .../elementary/tests/test_exponential.py | 8 +++-- sympy/integrals/tests/test_transforms.py | 20 +++++++---- sympy/series/tests/test_limits.py | 1 + sympy/simplify/simplify.py | 2 +- sympy/simplify/tests/test_simplify.py | 4 +-- sympy/solvers/tests/test_ode.py | 7 ++-- sympy/solvers/tests/test_solvers.py | 7 ++-- 8 files changed, 62 insertions(+), 21 deletions(-) diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py index edc258afbaff..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: @@ -705,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/tests/test_exponential.py b/sympy/functions/elementary/tests/test_exponential.py index e182bcdf8f03..5a410338e4a4 100644 --- a/sympy/functions/elementary/tests/test_exponential.py +++ b/sympy/functions/elementary/tests/test_exponential.py @@ -290,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/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/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/simplify.py b/sympy/simplify/simplify.py index 0815babb8c42..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 diff --git a/sympy/simplify/tests/test_simplify.py b/sympy/simplify/tests/test_simplify.py index ce6d8d2a23bc..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') @@ -724,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 1d83ba78cf77..9f0bc62d83db 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) @@ -1166,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(2)*I*sqrt(C1 + C2*exp(x))*exp(-x/2)), + Eq(f(x), sqrt(2)*I*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) diff --git a/sympy/solvers/tests/test_solvers.py b/sympy/solvers/tests/test_solvers.py index 4539d7e996e0..4b420f4693b3 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)] From ea7f967133e9dda6633c4cf413dc459f1a780fa9 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Wed, 14 Mar 2012 17:00:12 +0545 Subject: [PATCH 101/104] 3109: Pow.as_numer_denom power rules more accurate --- sympy/core/power.py | 51 ++++++++++++++--------------- sympy/core/tests/test_eval_power.py | 31 +++++++++++++++--- sympy/core/tests/test_expr.py | 5 --- sympy/solvers/tests/test_solvers.py | 6 ++-- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/sympy/core/power.py b/sympy/core/power.py index 1f8b270948c2..4e360ce8e515 100644 --- a/sympy/core/power.py +++ b/sympy/core/power.py @@ -678,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/tests/test_eval_power.py b/sympy/core/tests/test_eval_power.py index eb1277dbe4fc..e451c1ce38ad 100644 --- a/sympy/core/tests/test_eval_power.py +++ b/sympy/core/tests/test_eval_power.py @@ -76,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) @@ -119,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) @@ -127,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""" diff --git a/sympy/core/tests/test_expr.py b/sympy/core/tests/test_expr.py index a454124a1921..18c5ae568e84 100644 --- a/sympy/core/tests/test_expr.py +++ b/sympy/core/tests/test_expr.py @@ -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() == \ diff --git a/sympy/solvers/tests/test_solvers.py b/sympy/solvers/tests/test_solvers.py index 4b420f4693b3..297537c7bd43 100644 --- a/sympy/solvers/tests/test_solvers.py +++ b/sympy/solvers/tests/test_solvers.py @@ -638,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/ From 1071e58680e23dfcf16ebdca0daa3b44a0a985c0 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 15 Mar 2012 21:52:44 +0545 Subject: [PATCH 102/104] 3109: Mul._eval_power is more accurate The negative on the coefficient was being allowed to sneak out of the Rational power's args. --- sympy/core/mul.py | 6 +++--- sympy/core/tests/test_eval_power.py | 1 + sympy/geometry/tests/test_geometry.py | 1 - sympy/solvers/tests/test_ode.py | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sympy/core/mul.py b/sympy/core/mul.py index 1af3c4f4a045..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) diff --git a/sympy/core/tests/test_eval_power.py b/sympy/core/tests/test_eval_power.py index e451c1ce38ad..e3d8f6c8dd10 100644 --- a/sympy/core/tests/test_eval_power.py +++ b/sympy/core/tests/test_eval_power.py @@ -219,6 +219,7 @@ def test_issue_3109(): 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(): diff --git a/sympy/geometry/tests/test_geometry.py b/sympy/geometry/tests/test_geometry.py index 5ca5e8f7fa77..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 diff --git a/sympy/solvers/tests/test_ode.py b/sympy/solvers/tests/test_ode.py index 9f0bc62d83db..867420b0f9f5 100644 --- a/sympy/solvers/tests/test_ode.py +++ b/sympy/solvers/tests/test_ode.py @@ -1166,8 +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)*I*sqrt(C1 + C2*exp(x))*exp(-x/2)), - Eq(f(x), sqrt(2)*I*sqrt(C1 + C2*exp(x))*exp(-x/2))]) + 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) @@ -1178,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] From 856323b2ecd508d90a4f906b0c0adf33dbe86e43 Mon Sep 17 00:00:00 2001 From: Aleksandar Makelov Date: Tue, 20 Mar 2012 09:41:51 +0200 Subject: [PATCH 103/104] Fix of issue 2962: implemented correct presentations of D1 and D2. The dihedral groups Dn for n = 1 and n = 2 are exceptional in the sense that they cannot be embedded in the symmetric group Sn as all the other dihedral groups can. This was taken care of by explicitly providing the natural presentations of the two groups, as subgroups of S2 and S4 respectively. --- sympy/combinatorics/generators.py | 25 +++++++++++++++----- sympy/combinatorics/tests/test_generators.py | 3 +++ 2 files changed, 22 insertions(+), 6 deletions(-) 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/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])] From 9e3047e2899b8c57da16487816201c0a026f5402 Mon Sep 17 00:00:00 2001 From: Tom Bachmann Date: Tue, 20 Mar 2012 16:46:43 +0000 Subject: [PATCH 104/104] [integrals] Fix issue 3153. Slightly improve _split_mul in meijerint.py to recognise (b*(x-1/b) + 1)**a as (b*x)**a. Add a test regarding the particular integral of the issue. --- sympy/integrals/meijerint.py | 2 ++ sympy/integrals/tests/test_meijerint.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/sympy/integrals/meijerint.py b/sympy/integrals/meijerint.py index 8c20dc53d306..ec321c90d977 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_meijerint.py b/sympy/integrals/tests/test_meijerint.py index 17818cbc0d6a..ea8c66185035 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...