def test_random_func():
    """Tests the RandomFunction class"""
    center = 15
    amplitude = 2
    rf = RandomFunction(center=center, amplitude=amplitude)

    func = rf.gen_sample()

    for i in range(20):
        x = random.uniform(-10, 10)
        assert func(x) == func(x)
        assert center - amplitude <= func(x) <= center + amplitude
        assert not np.iscomplex(func(x))

    rf = RandomFunction(center=center, amplitude=amplitude, complex=True)

    func = rf.gen_sample()

    for i in range(20):
        x = random.uniform(-10, 10)
        assert func(x) == func(x)
        assert np.iscomplex(func(x))

    with raises(Exception, match="Expected 2 arguments, but received 1"):
        RandomFunction(input_dim=2).gen_sample()(1)

    with raises(Exception, match="Expected 1 arguments, but received 2"):
        RandomFunction(input_dim=1).gen_sample()(1, 2)
Esempio n. 2
0
def test_docs():
    """Make sure that the documentation examples work"""
    # Generate random real numbers between 3 and 7
    sampler = RealInterval(start=3, stop=7)
    # This is equivalent to
    sampler = RealInterval([3, 7])
    # The default is [1, 5]
    sampler = RealInterval()

    # Generate random integers between 3 and 7 inclusive
    sampler = IntegerRange(start=3, stop=7)
    # This is equivalent to
    sampler = IntegerRange([3, 7])
    # The default is [1, 5]
    sampler = IntegerRange()

    # Select random numbers from (1, 3, 5, 7, 9)
    sampler = DiscreteSet((1, 3, 5, 7, 9))
    # Always select 3.5
    sampler = DiscreteSet(3.5)

    # Select random complex numbers from 0 to 1 + i
    sampler = ComplexRectangle(re=[0, 1], im=[0, 1])
    # The default is re=[1, 3], im=[1, 3]
    sampler = ComplexRectangle()

    # Select random complex numbers from inside the unit circle
    sampler = ComplexSector(modulus=[0, 1], argument=[-np.pi, np.pi])
    # The default is modulus=[1, 3], argument=[0, pi/2]
    sampler = ComplexSector()

    # Test dependent sampling
    sampler = DependentSampler(depends=["x", "y", "z"],
                               formula="sqrt(x^2+y^2+z^2)")

    # Select either sin or cos randomly
    functionsampler = SpecificFunctions([np.cos, np.sin])
    # Always select a single lambda function
    functionsampler = SpecificFunctions(lambda x: x * x)

    # Generate a random function
    functionsampler = RandomFunction(center=1, amplitude=2)
    # The default is center=0, amplitude=10
    functionsampler = RandomFunction()

    # Generate a random sinusoid
    functionsampler = RandomFunction(num_terms=1)

    # Generate a function that takes in two values and outputs a 3D vector
    functionsampler = RandomFunction(input_dim=2, output_dim=3)
Esempio n. 3
0
def test_overriding_functions():
    grader = FormulaGrader(answers='z^2',
                           variables=['z'],
                           user_functions={
                               're': RandomFunction(),
                               'im': RandomFunction()
                           },
                           sample_from={'z': ComplexRectangle()})
    learner_input = 're(z)^2 - im(z)^2 + 2*i*re(z)*im(z)'
    assert not grader(None, learner_input)['ok']

    grader = FormulaGrader(answers='tan(1)',
                           user_functions={'sin': lambda x: x})
    assert grader(None, 'tan(1)')['ok']
    assert not grader(None, 'sin(1)/cos(1)')['ok']
Esempio n. 4
0
def test_ng_config():
    """Test that the NumericalGrader config bars unwanted entries"""
    expect = "not a valid value for dictionary value @ data\['failable_evals'\]. Got 1"
    with raises(Error, match=expect):
        NumericalGrader(answers="1", failable_evals=1)

    expect = "not a valid value for dictionary value @ data\['samples'\]. Got 2"
    with raises(Error, match=expect):
        NumericalGrader(answers="1", samples=2)

    expect = "not a valid value for dictionary value @ data\['variables'\]. Got \['x'\]"
    with raises(Error, match=expect):
        NumericalGrader(answers="1", variables=["x"])

    expect = "not a valid value for dictionary value @ data\['sample_from'\]. " + \
             "Got {'x': RealInterval\({'start': 1, 'stop': 5}\)}"
    with raises(Error, match=expect):
        NumericalGrader(answers="1", sample_from={"x": RealInterval()})

    expect = "not a valid value for dictionary value @ data\['user_functions'\]\['f'\]. " + \
             "Got RandomFunction"
    with raises(Error, match=expect):
        NumericalGrader(answers="1", user_functions={"f": RandomFunction()})

    expect = "not a valid value for dictionary value @ data\['user_functions'\]\['f'\]. " + \
             "Got \[<ufunc 'sin'>, <ufunc 'cos'>\]"
    with raises(Error, match=expect):
        NumericalGrader(answers="1", user_functions={"f": [np.sin, np.cos]})
Esempio n. 5
0
def test_fg_function_sampling():
    """Test random functions in FormulaGrader"""
    grader = FormulaGrader(answers="hello(x)",
                           variables=['x'],
                           user_functions={'hello': RandomFunction()})
    assert grader(None, 'hello(x)')['ok']
    assert isinstance(grader.random_funcs['hello'], RandomFunction)

    grader = FormulaGrader(answers="hello(x)",
                           variables=['x'],
                           user_functions={'hello': [lambda x: x * x]})
    assert isinstance(grader.random_funcs['hello'], SpecificFunctions)
    assert grader(None, 'hello(x)')['ok']

    grader = FormulaGrader(answers="hello(x)",
                           variables=['x'],
                           user_functions={'hello': [np.sin, np.cos, np.tan]})
    assert isinstance(grader.random_funcs['hello'], SpecificFunctions)
    assert grader(None, 'hello(x)')['ok']

    grader = FormulaGrader(
        answers="hello(x)",
        variables=['x'],
        user_functions={'hello': SpecificFunctions([np.sin, np.cos, np.tan])})
    assert isinstance(grader.random_funcs['hello'], SpecificFunctions)
    assert grader(None, 'hello(x)')['ok']
def test_ng_config():
    """Test that the NumericalGrader config bars unwanted entries"""
    expect = r"not a valid value for dictionary value @ data\[u?'failable_evals'\]. Got 1"
    with raises(Error, match=expect):
        NumericalGrader(answers="1", failable_evals=1)

    expect = r"not a valid value for dictionary value @ data\[u?'samples'\]. Got 2"
    with raises(Error, match=expect):
        NumericalGrader(answers="1", samples=2)

    expect = (r"length of value must be at most 0 for dictionary value "
              r"@ data\[u?'variables'\]. Got \[u?'x'\]")
    with raises(Error, match=expect):
        NumericalGrader(answers="1", variables=["x"])

    expect = (
        r"extra keys not allowed @ data\[u?'sample_from'\]\[u?'x'\]. Got "
        r"RealInterval\({u?'start': 1, u?'stop': 5}\)")
    with raises(Error, match=expect):
        NumericalGrader(answers="1", sample_from={"x": RealInterval()})

    expect = (
        r"not a valid value for dictionary value @ data\[u?'user_functions'\]\[u?'f'\]. "
        r"Got RandomFunction")
    with raises(Error, match=expect):
        NumericalGrader(answers="1", user_functions={"f": RandomFunction()})

    expect = (r"not a valid value for dictionary value @ data\[u?'user_functions'\]\[u?'f'\]. " + \
              r"Got \[<ufunc 'sin'>, <ufunc 'cos'>\]")
    with raises(Error, match=expect):
        NumericalGrader(answers="1", user_functions={"f": [np.sin, np.cos]})
def test_argument_number_of_RandomFunction():
    """Tests to make sure we can extract the number of inputs expected for a random function"""
    func = RandomFunction(input_dim=3).gen_sample()
    assert validatorfuncs.get_number_of_args(func) == 3
Esempio n. 8
0
def test_docs():
    """Test that the documentation examples work as expected"""
    grader = FormulaGrader(answers='1+x^2+y', variables=['x', 'y'])
    assert grader(None, '1+x^2+y')['ok']

    grader = FormulaGrader(answers='1+x^2+y+z/2',
                           variables=['x', 'y', 'z'],
                           sample_from={
                               'x': ComplexRectangle(),
                               'y': [2, 6],
                               'z': (1, 3, 4, 8)
                           })
    assert grader(None, '1+x^2+y+z/2')['ok']

    grader = FormulaGrader(answers='a_{0} + a_{1}*x + 1/2*a_{2}*x^2',
                           variables=['x'],
                           numbered_vars=['a'],
                           sample_from={
                               'x': [-5, 5],
                               'a': [-10, 10]
                           })
    assert grader(None, 'a_{0} + a_{1}*x + 1/2*a_{2}*x^2')['ok']

    grader = FormulaGrader(answers='1+x^2', variables=['x'], samples=10)
    assert grader(None, '1+x^2')['ok']

    grader = FormulaGrader(answers='1+x^2',
                           variables=['x'],
                           samples=10,
                           failable_evals=1)
    assert grader(None, '1+x^2')['ok']

    grader = FormulaGrader(answers='abs(z)^2',
                           variables=['z'],
                           sample_from={'z': ComplexRectangle()})
    assert grader(None, 'abs(z)^2')['ok']

    grader = FormulaGrader(answers='sqrt(1 - cos(x)^2)',
                           variables=['x'],
                           sample_from={'x': [0, np.pi]},
                           blacklist=['sin'])
    assert grader(None, 'sqrt(1 - cos(x)^2)')['ok']

    grader = FormulaGrader(answers='sin(x)/cos(x)',
                           variables=['x'],
                           whitelist=['sin', 'cos'])
    assert grader(None, 'sin(x)/cos(x)')['ok']

    grader = FormulaGrader(answers='pi/2-x', variables=['x'], whitelist=[None])
    assert grader(None, 'pi/2-x')['ok']

    grader = FormulaGrader(answers='2*sin(theta)*cos(theta)',
                           variables=['theta'],
                           required_functions=['sin', 'cos'])
    assert grader(None, '2*sin(theta)*cos(theta)')['ok']

    grader = FormulaGrader(answers='x*x',
                           variables=['x'],
                           user_functions={'f': lambda x: x * x})
    assert grader(None, 'x^2')['ok']

    grader = FormulaGrader(answers="f''(x)",
                           variables=['x'],
                           user_functions={"f''": lambda x: x * x})
    assert grader(None, "f''(x)")['ok']

    grader = FormulaGrader(answers="x^2",
                           variables=['x'],
                           user_functions={"sin": lambda x: x * x},
                           suppress_warnings=True)
    assert grader(None, 'sin(x)')['ok']

    grader = FormulaGrader(answers="f(x)",
                           variables=['x'],
                           user_functions={"f": [np.sin, np.cos]})
    assert grader(None, 'f(x)')['ok']

    grader = FormulaGrader(answers="f''(x) + omega^2*f(x)",
                           variables=['x', 'omega'],
                           user_functions={
                               "f": RandomFunction(),
                               "f''": RandomFunction()
                           })
    assert grader(None, "f''(x)+omega^2*f(x)")['ok']

    grader = FormulaGrader(answers='1/sqrt(1-v^2/c^2)',
                           variables=['v'],
                           user_constants={'c': 3e8})
    assert grader(None, '1/sqrt(1-v^2/c^2)')['ok']

    grader = FormulaGrader(
        answers='2*sin(theta)*cos(theta)',
        variables=['theta'],
        forbidden_strings=[
            '*theta', 'theta*', 'theta/', '+theta', 'theta+', '-theta',
            'theta-'
        ],
        forbidden_message=
        "Your answer should only use trigonometric functions acting on theta, not multiples of theta"
    )
    assert grader(None, '2*sin(theta)*cos(theta)')['ok']

    grader = FormulaGrader(answers='2*sin(theta)*cos(theta)',
                           variables=['theta'],
                           tolerance=0.00001)
    assert grader(None, '2*sin(theta)*cos(theta)')['ok']

    grader = FormulaGrader(answers='2*m',
                           variables=['m'],
                           metric_suffixes=True)
    assert grader(None, '2*m')['ok']
    assert not grader(None, '2m')['ok']

    def is_coterminal(comparer_params_evals, student_eval, utils):
        answer = comparer_params_evals[0]
        reduced = student_eval % (360)
        return utils.within_tolerance(answer, reduced)

    grader = FormulaGrader(answers={
        'comparer': is_coterminal,
        'comparer_params': ['b^2/a'],
    },
                           variables=['a', 'b'],
                           tolerance='1%')
    assert grader(None, 'b^2/a')['ok']
    assert grader(None, 'b^2/a + 720')['ok']
    assert not grader(None, 'b^2/a + 225')['ok']
Esempio n. 9
0
def test_fg_debug_log():
    set_seed(0)
    grader = FormulaGrader(answers='x^2 + f(y) + z',
                           variables=['x', 'y', 'z'],
                           sample_from={'z': ComplexRectangle()},
                           blacklist=['sin', 'cos', 'tan'],
                           user_functions={
                               'f': RandomFunction(),
                               'square': lambda x: x**2
                           },
                           samples=2,
                           debug=True)
    result = grader(None, 'z + x*x + f(y)')

    message = (
        "<pre>MITx Grading Library Version {version}<br/>\n"
        "Student Response:<br/>\n"
        "z + x*x + f(y)<br/>\n"
        "<br/>\n"
        "==============================================================<br/>\n"
        "FormulaGrader Debug Info<br/>\n"
        "==============================================================<br/>\n"
        "Functions available during evaluation and allowed in answer:<br/>\n"
        "{{   'abs': <function absolute at 0x...>,<br/>\n"
        "    'arccos': <function arccos at 0x...>,<br/>\n"
        "    'arccosh': <function arccosh at 0x...>,<br/>\n"
        "    'arccot': <function arccot at 0x...>,<br/>\n"
        "    'arccoth': <function arccoth at 0x...>,<br/>\n"
        "    'arccsc': <function arccsc at 0x...>,<br/>\n"
        "    'arccsch': <function arccsch at 0x...>,<br/>\n"
        "    'arcsec': <function arcsec at 0x...>,<br/>\n"
        "    'arcsech': <function arcsech at 0x...>,<br/>\n"
        "    'arcsin': <function arcsin at 0x...>,<br/>\n"
        "    'arcsinh': <function arcsinh at 0x...>,<br/>\n"
        "    'arctan': <function arctan at 0x...>,<br/>\n"
        "    'arctan2': <function arctan2 at 0x...>,<br/>\n"
        "    'arctanh': <function arctanh at 0x...>,<br/>\n"
        "    'conj': <ufunc 'conjugate'>,<br/>\n"
        "    'cosh': <function cosh at 0x...>,<br/>\n"
        "    'cot': <function cot at 0x...>,<br/>\n"
        "    'coth': <function coth at 0x...>,<br/>\n"
        "    'csc': <function csc at 0x...>,<br/>\n"
        "    'csch': <function csch at 0x...>,<br/>\n"
        "    'exp': <function exp at 0x...>,<br/>\n"
        "    'f': <function f at 0x...>,<br/>\n"
        "    'fact': <function factorial at 0x...>,<br/>\n"
        "    'factorial': <function factorial at 0x...>,<br/>\n"
        "    'im': <function imag at 0x...>,<br/>\n"
        "    'kronecker': <function kronecker at 0x...>,<br/>\n"
        "    'ln': <function log at 0x...>,<br/>\n"
        "    'log10': <function log10 at 0x...>,<br/>\n"
        "    'log2': <function log2 at 0x...>,<br/>\n"
        "    're': <function real at 0x...>,<br/>\n"
        "    'sec': <function sec at 0x...>,<br/>\n"
        "    'sech': <function sech at 0x...>,<br/>\n"
        "    'sinh': <function sinh at 0x...>,<br/>\n"
        "    'sqrt': <function sqrt at 0x...>,<br/>\n"
        "    'square': <function <lambda> at 0x...>,<br/>\n"
        "    'tanh': <function tanh at 0x...>}}<br/>\n"
        "Functions available during evaluation and disallowed in answer:<br/>\n"
        "{{   'cos': <function cos at 0x...>,<br/>\n"
        "    'sin': <function sin at 0x...>,<br/>\n"
        "    'tan': <function tan at 0x...>}}<br/>\n"
        "<br/>\n"
        "<br/>\n"
        "==========================================<br/>\n"
        "Evaluation Data for Sample Number 1 of 2<br/>\n"
        "==========================================<br/>\n"
        "Variables:<br/>\n"
        "{{   'e': 2.718281828459045,<br/>\n"
        "    'i': 1j,<br/>\n"
        "    'j': 1j,<br/>\n"
        "    'pi': 3.141592653589793,<br/>\n"
        "    'x': 3.195254015709299,<br/>\n"
        "    'y': 3.860757465489678,<br/>\n"
        "    'z': (2.205526752143288+2.0897663659937935j)}}<br/>\n"
        "Student Eval: (14.7111745179+2.08976636599j)<br/>\n"
        "Compare to:  [(14.711174517877566+2.0897663659937935j)]<br/>\n"
        "Comparer Function: <function equality_comparer at 0x...><br/>\n"
        "Comparison Result: {{   'grade_decimal': 1.0, 'msg': '', 'ok': True}}<br/>\n"
        "<br/>\n"
        "<br/>\n"
        "==========================================<br/>\n"
        "Evaluation Data for Sample Number 2 of 2<br/>\n"
        "==========================================<br/>\n"
        "Variables:<br/>\n"
        "{{   'e': 2.718281828459045,<br/>\n"
        "    'i': 1j,<br/>\n"
        "    'j': 1j,<br/>\n"
        "    'pi': 3.141592653589793,<br/>\n"
        "    'x': 2.694619197355619,<br/>\n"
        "    'y': 3.5835764522666245,<br/>\n"
        "    'z': (1.875174422525385+2.7835460015641598j)}}<br/>\n"
        "Student Eval: (11.9397106851+2.78354600156j)<br/>\n"
        "Compare to:  [(11.93971068506166+2.7835460015641598j)]<br/>\n"
        "Comparer Function: <function equality_comparer at 0x...><br/>\n"
        "Comparison Result: {{   'grade_decimal': 1.0, 'msg': '', 'ok': True}}<br/>\n"
        "</pre>").format(version=VERSION)
    assert result['msg'] == message
Esempio n. 10
0
def test_docs():
    """Test that the documentation examples work as expected"""
    grader = FormulaGrader(answers='1+x^2+y', variables=['x', 'y'])
    assert grader(None, '1+x^2+y')['ok']

    grader = FormulaGrader(answers='1+x^2+y+z/2',
                           variables=['x', 'y', 'z'],
                           sample_from={
                               'x': ComplexRectangle(),
                               'y': [2, 6],
                               'z': (1, 3, 4, 8)
                           })
    assert grader(None, '1+x^2+y+z/2')['ok']

    grader = FormulaGrader(answers='1+x^2', variables=['x'], samples=10)
    assert grader(None, '1+x^2')['ok']

    grader = FormulaGrader(answers='1+x^2',
                           variables=['x'],
                           samples=10,
                           failable_evals=1)
    assert grader(None, '1+x^2')['ok']

    grader = FormulaGrader(answers='abs(z)^2',
                           variables=['z'],
                           sample_from={'z': ComplexRectangle()})
    assert grader(None, 'abs(z)^2')['ok']

    grader = FormulaGrader(answers='sqrt(1 - cos(x)^2)',
                           variables=['x'],
                           sample_from={'x': [0, np.pi]},
                           blacklist=['sin'])
    assert grader(None, 'sqrt(1 - cos(x)^2)')['ok']

    grader = FormulaGrader(answers='sin(x)/cos(x)',
                           variables=['x'],
                           whitelist=['sin', 'cos'])
    assert grader(None, 'sin(x)/cos(x)')['ok']

    grader = FormulaGrader(answers='pi/2-x', variables=['x'], whitelist=[None])
    assert grader(None, 'pi/2-x')['ok']

    grader = FormulaGrader(answers='2*sin(theta)*cos(theta)',
                           variables=['theta'],
                           required_functions=['sin', 'cos'])
    assert grader(None, '2*sin(theta)*cos(theta)')['ok']

    grader = FormulaGrader(answers='x*x',
                           variables=['x'],
                           user_functions={'f': lambda x: x * x})
    assert grader(None, 'x^2')['ok']

    grader = FormulaGrader(answers="f''(x)",
                           variables=['x'],
                           user_functions={"f''": lambda x: x * x})
    assert grader(None, "f''(x)")['ok']

    grader = FormulaGrader(answers="x^2",
                           variables=['x'],
                           user_functions={"sin": lambda x: x * x})
    assert grader(None, 'sin(x)')['ok']

    grader = FormulaGrader(answers="f(x)",
                           variables=['x'],
                           user_functions={"f": [np.sin, np.cos]})
    assert grader(None, 'f(x)')['ok']

    grader = FormulaGrader(answers="f''(x) + omega^2*f(x)",
                           variables=['x', 'omega'],
                           user_functions={
                               "f": RandomFunction(),
                               "f''": RandomFunction()
                           })
    assert grader(None, "f''(x)+omega^2*f(x)")['ok']

    grader = FormulaGrader(answers='1/sqrt(1-v^2/c^2)',
                           variables=['v'],
                           user_constants={'c': 3e8})
    assert grader(None, '1/sqrt(1-v^2/c^2)')['ok']

    grader = FormulaGrader(
        answers='2*sin(theta)*cos(theta)',
        variables=['theta'],
        forbidden_strings=[
            '*theta', 'theta*', 'theta/', '+theta', 'theta+', '-theta',
            'theta-'
        ],
        forbidden_message=
        "Your answer should only use trigonometric functions acting on theta, not multiples of theta"
    )
    assert grader(None, '2*sin(theta)*cos(theta)')['ok']

    grader = FormulaGrader(answers='2*sin(theta)*cos(theta)',
                           variables=['theta'],
                           tolerance=0.00001)
    assert grader(None, '2*sin(theta)*cos(theta)')['ok']

    grader = FormulaGrader(answers='2*sin(theta)*cos(theta)',
                           variables=['theta'],
                           case_sensitive=False)
    assert grader(None, '2*Sin(theta)*Cos(theta)')['ok']

    grader = FormulaGrader(answers='2*m',
                           variables=['m'],
                           metric_suffixes=True)
    assert grader(None, '2*m')['ok']
    assert not grader(None, '2m')['ok']
def test_fg_debug_log():
    set_seed(0)
    grader = FormulaGrader(answers='x^2 + f(y) + z',
                           variables=['x', 'y', 'z'],
                           sample_from={'z': ComplexRectangle()},
                           blacklist=['sin', 'cos', 'tan'],
                           user_functions={
                               'f': RandomFunction(),
                               'square': squareit
                           },
                           samples=2,
                           debug=True)
    result = grader(None, 'z + x*x + f(y)')

    message = (
        "<pre>MITx Grading Library Version {version}<br/>\n"
        "Running on edX using python {python_version}<br/>\n"
        "Student Response:<br/>\n"
        "z + x*x + f(y)<br/>\n"
        "<br/>\n"
        "==============================================================<br/>\n"
        "FormulaGrader Debug Info<br/>\n"
        "==============================================================<br/>\n"
        "Functions available during evaluation and allowed in answer:<br/>\n"
        "{{{u}'abs': <function absolute at 0x...>,<br/>\n"
        " {u}'arccos': <function arccos at 0x...>,<br/>\n"
        " {u}'arccosh': <function arccosh at 0x...>,<br/>\n"
        " {u}'arccot': <function arccot at 0x...>,<br/>\n"
        " {u}'arccoth': <function arccoth at 0x...>,<br/>\n"
        " {u}'arccsc': <function arccsc at 0x...>,<br/>\n"
        " {u}'arccsch': <function arccsch at 0x...>,<br/>\n"
        " {u}'arcsec': <function arcsec at 0x...>,<br/>\n"
        " {u}'arcsech': <function arcsech at 0x...>,<br/>\n"
        " {u}'arcsin': <function arcsin at 0x...>,<br/>\n"
        " {u}'arcsinh': <function arcsinh at 0x...>,<br/>\n"
        " {u}'arctan': <function arctan at 0x...>,<br/>\n"
        " {u}'arctan2': <function arctan2 at 0x...>,<br/>\n"
        " {u}'arctanh': <function arctanh at 0x...>,<br/>\n"
        " {u}'ceil': <function ceil at 0x...>,<br/>\n"
        " {u}'conj': &lt;ufunc 'conjugate'&gt;,<br/>\n"
        " {u}'cosh': <function cosh at 0x...>,<br/>\n"
        " {u}'cot': <function cot at 0x...>,<br/>\n"
        " {u}'coth': <function coth at 0x...>,<br/>\n"
        " {u}'csc': <function csc at 0x...>,<br/>\n"
        " {u}'csch': <function csch at 0x...>,<br/>\n"
        " {u}'exp': <function exp at 0x...>,<br/>\n"
        " {u}'f': <function random_function at 0x...>,<br/>\n"
        " {u}'fact': <function factorial at 0x...>,<br/>\n"
        " {u}'factorial': <function factorial at 0x...>,<br/>\n"
        " {u}'floor': <function floor at 0x...>,<br/>\n"
        " {u}'im': <function imag at 0x...>,<br/>\n"
        " {u}'kronecker': <function kronecker at 0x...>,<br/>\n"
        " {u}'ln': <function log at 0x...>,<br/>\n"
        " {u}'log10': <function log10 at 0x...>,<br/>\n"
        " {u}'log2': <function log2 at 0x...>,<br/>\n"
        " {u}'max': <function max at 0x...>,<br/>\n"
        " {u}'min': <function min at 0x...>,<br/>\n"
        " {u}'re': <function real at 0x...>,<br/>\n"
        " {u}'sec': <function sec at 0x...>,<br/>\n"
        " {u}'sech': <function sech at 0x...>,<br/>\n"
        " {u}'sinh': <function sinh at 0x...>,<br/>\n"
        " {u}'sqrt': <function sqrt at 0x...>,<br/>\n"
        " {u}'square': <function squareit at 0x...>,<br/>\n"
        " {u}'tanh': <function tanh at 0x...>}}<br/>\n"
        "Functions available during evaluation and disallowed in answer:<br/>\n"
        "{{{u}'cos': <function cos at 0x...>,<br/>\n"
        " {u}'sin': <function sin at 0x...>,<br/>\n"
        " {u}'tan': <function tan at 0x...>}}<br/>\n"
        "<br/>\n"
        "<br/>\n"
        "==========================================<br/>\n"
        "Evaluation Data for Sample Number 1 of 2<br/>\n"
        "==========================================<br/>\n"
        "Variables:<br/>\n"
        "{{{u}'e': 2.718281828459045,<br/>\n"
        " {u}'i': 1j,<br/>\n"
        " {u}'j': 1j,<br/>\n"
        " {u}'pi': 3.141592653589793,<br/>\n"
        " {u}'x': 3.195254015709299,<br/>\n"
        " {u}'y': 3.860757465489678,<br/>\n"
        " {u}'z': (2.205526752143288+2.0897663659937935j)}}<br/>\n"
        "Student Eval: (14.7111745179+2.08976636599j)<br/>\n"
        "Compare to:  [(14.711174517877566+2.0897663659937935j)]<br/>\n"
        "<br/>\n"
        "<br/>\n"
        "==========================================<br/>\n"
        "Evaluation Data for Sample Number 2 of 2<br/>\n"
        "==========================================<br/>\n"
        "Variables:<br/>\n"
        "{{{u}'e': 2.718281828459045,<br/>\n"
        " {u}'i': 1j,<br/>\n"
        " {u}'j': 1j,<br/>\n"
        " {u}'pi': 3.141592653589793,<br/>\n"
        " {u}'x': 2.694619197355619,<br/>\n"
        " {u}'y': 3.5835764522666245,<br/>\n"
        " {u}'z': (1.875174422525385+2.7835460015641598j)}}<br/>\n"
        "Student Eval: (11.9397106851+2.78354600156j)<br/>\n"
        "Compare to:  [(11.93971068506166+2.7835460015641598j)]<br/>\n"
        "<br/>\n"
        "<br/>\n"
        "==========================================<br/>\n"
        "Comparison Data for All 2 Samples<br/>\n"
        "==========================================<br/>\n"
        "Comparer Function: EqualityComparer({{{u}'transform': <function identity_transform at 0x...>}})<br/>\n"
        "Comparison Results:<br/>\n"
        "[{{{u}'grade_decimal': 1.0, {u}'msg': {u}'', {u}'ok': True}},<br/>\n"
        " {{{u}'grade_decimal': 1.0, {u}'msg': {u}'', {u}'ok': True}}]<br/>\n"
        "</pre>").format(version=VERSION,
                         python_version=platform.python_version(),
                         u=UNICODE_PREFIX)
    message = message.replace("<func", "&lt;func").replace("...>", "...&gt;")
    expected = round_decimals_in_string(message)
    result_msg = round_decimals_in_string(result['msg']).replace(
        'test_fg_debug_log.<locals>.', '')
    assert expected == result_msg