Ticket #10952: noise.patch

File noise.patch, 5.0 KB (added by robertwb, 9 years ago)
  • sage-doctest

    # HG changeset patch
    # User Robert Bradshaw <robertwb@math.washington.edu>
    # Date 1300349461 25200
    # Node ID a39594c47e9a49cbd7d2006322a04b16c031a7e4
    # Parent  4047e578febc6e9895d6b5650dc81f0515a170c5
    #10952 - better handling of numerial noise in doctests
    
    diff -r 4047e578febc -r a39594c47e9a sage-doctest
    a b  
    216216    sys.exit(runner.failures)
    217217""" % dict
    218218
    219 NONE=0; LONG_TIME=1; RANDOM=2; OPTIONAL=3; NOT_IMPLEMENTED=4; NOT_TESTED=5
     219NONE=0; LONG_TIME=1; RANDOM=2; OPTIONAL=3; NOT_IMPLEMENTED=4; NOT_TESTED=5; TOLERANCE=6
     220tolerance_pattern = re.compile('((?:abs(?:solute)?)|(?:rel(?:ative)?))? *?tol(?:erance)?( +[0-9.e+-]+)?')
    220221
    221222def comment_modifier(s):
    222223    sind = s.find('#')
     
    235236        v.append(NOT_TESTED)
    236237    if 'random' in L:
    237238        v.append(RANDOM)
     239    m = tolerance_pattern.search(L)
     240    if m:
     241        v.append(TOLERANCE)
     242        rel_or_abs, epsilon = m.groups()
     243        if rel_or_abs is not None:
     244            rel_or_abs = rel_or_abs[:3]
     245        if epsilon is None:
     246            epsilon = 1e-15
     247        else:
     248            epsilon = float(epsilon.strip())
     249        v.append((rel_or_abs, epsilon))
    238250    return v, L
    239251
    240252def preparse_line_with_prompt(L):
     
    255267    # t: Deal with code whose output should be ignored.
    256268    t = []
    257269
    258     # following two: used only for parsing only_optional; list of comments
    259     comment_modifiers = [] 
     270    # following three: used only for parsing only_optional; list of comments
     271    has_tolerance = False
     272    comment_modifiers = []
    260273    last_prompt_comment = ''
    261274   
    262275    for L in s.splitlines():
    263276        begin = L.lstrip()[:5]
    264277        comment = ''
    265278        if begin == 'sage:':
     279            if has_tolerance:
     280                rel_or_abs, epsilon = c[c.index(TOLERANCE) + 1]
     281                t.append("... ''', res, %r%s)" % (epsilon, '' if rel_or_abs is None else ", '%s'" % rel_or_abs))
    266282            c, comment = comment_modifier(L)
    267283            last_prompt_comment = comment
    268284            line = ''
     
    276292                L = '\n'   # not tested
    277293            if OPTIONAL in c and not (only_optional or optional):
    278294                L = '\n'
     295            if TOLERANCE in c:
     296                t.append(">>> res = Exception")
     297                L = "sage: res = %s" % L.lstrip()[5:]
     298                has_tolerance = True
     299            else:
     300                has_tolerance = False
    279301            line = preparse_line_with_prompt(L)
    280302            if RANDOM in c:
    281303                # count spaces at the beginning of line to fix alignment later
     
    285307                # append a line saying 'ignore' followed by ellipsis (...)
    286308                # and an empty line, to ignore the output given in the test
    287309                line += '\n' + ' '*i + 'ignore ...\n'
     310            if has_tolerance:
     311                line += "\n>>> check_with_tolerance('''"
    288312            t.append(line)
    289313           
    290314        elif begin.startswith('...'):
     
    294318           
    295319        else:
    296320            comment = last_prompt_comment
     321            if has_tolerance:
     322                L = "... " + L
    297323            t.append(L)
    298324       
    299325        comment_modifiers.append(comment)
    300326
     327    if has_tolerance:
     328        rel_or_abs, epsilon = c[c.index(TOLERANCE) + 1]
     329        t.insert(-1, "... ''', res, %r%s)" % (epsilon, '' if rel_or_abs is None else ", '%s'" % rel_or_abs))
     330
    301331    # The very last line -- which is typically """ -- must never be marked as optional,
    302332    # or it might not get included, which would be a syntax error.
    303333    comment_modifiers[-1] = ''
     
    419449    s = "# -*- coding: utf-8 -*-\n"
    420450    s += "from sage.all_cmdline import *; \n"
    421451    s += "import sage.plot.plot; sage.plot.plot.DOCTEST_MODE=True\n"  # turn off image popup
    422     s += """
     452    s += r"""
    423453def warning_function(f):
    424454    import warnings
    425455
     
    433463def change_warning_output(file):
    434464    import warnings
    435465    warnings.showwarning = warning_function(file)
     466
     467import re
     468float_pattern = re.compile('[+-]?((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?')
     469
     470def check_with_tolerance(expected, actual, epsilon, rel_or_abs=None):
     471    if actual is Exception:
     472        return # error computing actual
     473    else:
     474        actual = str(actual)
     475    expected = re.sub('\n +', '\n', expected)
     476    actual = re.sub('\n +', '\n', actual)
     477    assert float_pattern.sub('#', expected.strip()) == float_pattern.sub('#', actual.strip()), \
     478        "Expected '" + expected + "' got '" + actual + "'"
     479    for expected_value, actual_value in zip(float_pattern.finditer(expected), float_pattern.finditer(actual)):
     480        expected_value = float(expected_value.group())
     481        actual_value = float(actual_value.group())
     482        if rel_or_abs == 'abs' or expected_value == 0:
     483            assert abs(expected_value - actual_value) < epsilon, (expected_value, actual_value)
     484        else:
     485            assert abs((expected_value - actual_value) / expected_value) < epsilon, "Out of tolerance %s vs %s" % (expected_value, actual_value)
    436486"""
    437487
    438488    if not library_code: