Beispiel #1
0
def test_shape_errors_false_grades_incorrect():
    grader0 = MatrixGrader(answers='A*B',
                           variables=['A', 'B'],
                           sample_from={
                               'A': RealMatrices(shape=[2, 3]),
                               'B': RealMatrices(shape=[3, 2])
                           })
    grader1 = MatrixGrader(answers='A*B',
                           variables=['A', 'B'],
                           sample_from={
                               'A': RealMatrices(shape=[2, 3]),
                               'B': RealMatrices(shape=[3, 2])
                           },
                           shape_errors=False)

    msg = ("Cannot add/subtract a matrix of shape (rows: 2, cols: 3) with "
           "a matrix of shape (rows: 3, cols: 2).")
    with raises(ShapeError, match=re.escape(msg)):
        grader0(None, 'A + B')

    assert grader1(None, 'A + B') == {
        'ok': False,
        'msg': msg,
        'grade_decimal': 0
    }
Beispiel #2
0
def test_default_grader_instance_override():
    grader_0 = MatrixGrader()
    grader_1 = MatrixGrader(entry_partial_credit='proportional')
    grader_2 = MatrixGrader()

    # not the same grader...
    assert grader_0 is not grader_2
    # but should use the exact same comparer function, class default
    assert grader_0.default_comparer is grader_2.default_comparer
    # but grader_1 should use an instance-specific default_comparer
    assert grader_0.default_comparer is not grader_1.default_comparer
Beispiel #3
0
def test_entry_partial_flat_rate_credit_grading():
    grader = MatrixGrader(max_array_dim=2,
                          answers='[[0, 1, 2], [x, y, z]]',
                          variables=['x', 'y', 'z'],
                          entry_partial_credit=0.123)
    expected = {
        'ok':
        'partial',
        'msg':
        six.u('Some array entries are incorrect, marked below:<br/>\n'
              '<pre>'
              '[[{g} {b} {b}]<br/>'
              ' [{b} {b} {g}]]'
              '</pre>').format(g=GOOD, b=BAD),
        'grade_decimal':
        0.123
    }
    assert grader(None, '[[0, 10, 20*z], [x/2, y^2, z]]') == expected
    assert grader(None, '[[0, 1, 2], [x, y, z]]') == {
        'ok': True,
        'msg': '',
        'grade_decimal': 1
    }
    assert grader(None, '[[10, 20, 30], [40, 50, 60]]') == {
        'ok':
        False,
        'msg':
        six.u('Some array entries are incorrect, marked below:<br/>\n'
              '<pre>'
              '[[{b} {b} {b}]<br/>'
              ' [{b} {b} {b}]]'
              '</pre>').format(g=GOOD, b=BAD),
        'grade_decimal':
        0
    }
Beispiel #4
0
def test_max_array_dim():
    grader = MatrixGrader(answers="[1, 2, 3]")
    # by default, student cannot enter matrices entry-by-entry
    match = "Matrix expressions have been forbidden in this entry."
    with raises(UnableToParse, match=match):
        grader(None, "[[1, 2, 3]]")

    # But authors can. This allows authors to specify a matrix in comparer_params
    # when students are required to input an eigenvector
    grader = MatrixGrader(answers="[[1, 2, 3]]")
    match = "Expected answer to be a matrix, but input is a scalar"
    with raises(InputTypeError, match=match):
        grader(None, "0")

    grader = MatrixGrader(answers="[[1, 2, 3]]", max_array_dim=2)
    assert grader(None, "[[1, 2, 3]]")['ok']
Beispiel #5
0
def test_matrix_inverses_raise_error_if_disabled():
    grader = MatrixGrader(answers='A^2',
                          variables=['A'],
                          sample_from={'A': RealMatrices()},
                          negative_powers=False)

    match = 'Negative matrix powers have been disabled.'
    with raises(MathArrayError, match=match):
        grader(None, 'A^3*A^-1')['ok']
Beispiel #6
0
def test_entry_partial_custom_message():
    grader = MatrixGrader(max_array_dim=2,
                          answers='[[1, 2], [3, 4]]',
                          entry_partial_credit='proportional',
                          entry_partial_msg='Partly correct')
    grader_with_locs = MatrixGrader(
        max_array_dim=2,
        answers='[[1, 2], [3, 4]]',
        entry_partial_credit='proportional',
        entry_partial_msg=six.u(
            'Partly correct, errors at\n{error_locations}'))

    assert grader(None, '[[1, 20], [30, 4]]')['msg'] == 'Partly correct'

    formatted_msg = six.u('Partly correct, errors at<br/>\n'
                          '<pre>'
                          '[[{g} {b}]<br/>'
                          ' [{b} {g}]]'
                          '</pre>').format(g=GOOD, b=BAD)
    result_msg = grader_with_locs(None, '[[1, 20], [30, 4]]')['msg']
    assert result_msg == formatted_msg
Beispiel #7
0
def test_wrong_answer_type_error_messages_with_scalars():
    """
    Check that answer shape errors are raised correctly when answer or student_input
    is a scalar.

    These are worth checking separately because the numbers do not have 'shape'
    attributes.
    """

    grader = MatrixGrader(answers='[1, 2, 3]',
                          max_array_dim=2,
                          answer_shape_mismatch=dict(is_raised=True,
                                                     msg_detail='type'))
    match = 'Expected answer to be a vector, but input is a scalar'
    with raises(InputTypeError, match=match):
        grader(None, '10')

    grader = MatrixGrader(answers='10',
                          max_array_dim=2,
                          answer_shape_mismatch=dict(is_raised=True,
                                                     msg_detail='type'))
    match = 'Expected answer to be a scalar, but input is a vector'
    with raises(InputTypeError, match=match):
        grader(None, '[1, 2, 3]')
Beispiel #8
0
def test_entry_partial_message_with_vectors():
    grader = MatrixGrader(answers='[0, 1, 2, 3, 4]',
                          entry_partial_credit='proportional')

    expected = {
        'ok':
        'partial',
        'grade_decimal':
        0.6,
        'msg':
        six.u('Some array entries are incorrect, marked below:<br/>\n'
              '<pre>'
              '[{g} {b} {g} {g} {b}]'
              '</pre>').format(g=GOOD, b=BAD)
    }
    result = grader(None, '[0, 10, 2, 3, 40]')
    assert expected == result
Beispiel #9
0
def test_suppress_matrix_messages():
    grader = MatrixGrader(
        answers='[1, 2, 3]',
        answer_shape_mismatch=dict(
            is_raised=True,  # Overridden by suppress_matrix_messages
        ),
        shape_errors=True,  # Overridden by suppress_matrix_messages
        suppress_matrix_messages=True)
    assert grader(None, '10')['ok'] is False
    assert grader(None, '10')['msg'] == ''
    assert grader(None, '[1, 2, 3] + 1')['ok'] is False
    assert grader(None, '[1, 2, 3] + 1')['msg'] == ''
    assert grader(None, '[1, 2, 3, 4]')['ok'] is False
    assert grader(None, '[1, 2, 3, 4]')['msg'] == ''
    assert grader(None, 'sin([1, 2, 3])')['ok'] is False
    assert grader(None, '[1, 2, 3]^1.3')['ok'] is False

    # Note that we haven't suppressed all errors:
    with raises(ArgumentError):
        grader(None, 'sin(1, 2)')
Beispiel #10
0
def test_works_with_matrixgrader():
    grader = MatrixGrader(answers={
        'comparer_params': ['x*A*B^2'],
        'comparer':
        LinearComparer(proportional=0.6, offset=0.4, linear=0.2)
    },
                          variables=['x', 'A', 'B'],
                          sample_from={
                              'A': RealMatrices(),
                              'B': RealMatrices()
                          },
                          max_array_dim=2)

    assert grader(None, 'x*A*B^2')['grade_decimal'] == 1.0
    assert grader(None, '2*x*A*B^2')['grade_decimal'] == 0.6
    assert grader(None, 'x*A*B^2 + 5*[[1, 1], [1, 1]]')['grade_decimal'] == 0.4
    assert grader(None,
                  '3*x*A*B^2 + 5*[[1, 1], [1, 1]]')['grade_decimal'] == 0.2
    assert grader(None, 'x*A*B^2 + x*[[1, 1], [1, 1]]')['grade_decimal'] == 0
    assert grader(None, '0*A')['grade_decimal'] == 0
Beispiel #11
0
def test_fg_with_arrays():
    grader = MatrixGrader(answers='x*A*B*u + z*C^3*v/(u*C*v)',
                          variables=['A', 'B', 'C', 'u', 'v', 'z', 'x'],
                          sample_from={
                              'A': RealMatrices(shape=[2, 3]),
                              'B': RealMatrices(shape=[3, 2]),
                              'C': RealMatrices(shape=[2, 2]),
                              'u': RealVectors(shape=[2]),
                              'v': RealVectors(shape=[2]),
                              'z': ComplexRectangle()
                          },
                          identity_dim=2)

    correct_0 = 'x*A*B*u + z*C^3*v/(u*C*v)'
    correct_1 = 'z*C^3*v/(u*C*v) + x*A*B*u'
    correct_2 = '(1/16)* z*(2*I)*(2*C)^3*v/(u*C*v) + x*A*B*u'
    correct_3 = '(1/16)* z*(2*I)*(2*C)^3*v/(v*trans(C)*u) + x*A*B*u/2 + 0.5*x*A*B*u'

    assert grader(None, correct_0)['ok']
    assert grader(None, correct_1)['ok']
    assert grader(None, correct_2)['ok']
    assert grader(None, correct_3)['ok']

    match = r"Cannot multiply a matrix of shape \(rows: 3, cols: 2\) with a matrix of shape \(rows: 3, cols: 2\)"
    with raises(ShapeError, match=match):
        grader(None, 'B*B')

    match = "Cannot raise a non-square matrix to powers."
    with raises(ShapeError, match=match):
        grader(None, 'B^2')

    match = "Cannot add/subtract scalars to a matrix."
    with raises(ShapeError, match=match):
        grader(None, 'B + 5')

    match = (r"There was an error evaluating function sin\(...\)<br/>"
             r"1st input has an error: received a matrix of shape "
             r"\(rows: 3, cols: 2\), expected a scalar")
    with raises(DomainError, match=match):
        grader(None, 'sin(B)')
Beispiel #12
0
def test_entry_partial_message_with_tensors():
    grader = MatrixGrader(max_array_dim=3,
                          answers='[ [[0, 1], [2, 3]], [[4, 5], [6, 7]] ]',
                          entry_partial_credit='proportional')

    expected = {
        'ok':
        'partial',
        'grade_decimal':
        7 / 8,
        'msg':
        six.u('Some array entries are incorrect, marked below:<br/>\n'
              '<pre>'
              '[[[{g} {g}]<br/>'
              ' [{g} {b}]]<br/>'
              '<br/>'
              ' [[{g} {g}]<br/>'
              ' [{g} {g}]]]'
              '</pre>').format(g=GOOD, b=BAD)
    }
    result = grader(None, '[ [[0, 1], [2, 10]], [[4, 5], [6, 7]] ]')
    assert expected == result
Beispiel #13
0
def test_validate_student_input_shape_edge_case():
    with raises(AttributeError):
        MatrixGrader.validate_student_input_shape([1, 2], (2, ), 'type')
Beispiel #14
0
def test_identity_dim_provides_identity():
    no_identity = MatrixGrader()
    assert no_identity.constants.get('I', None) is None

    grader = MatrixGrader(identity_dim=4)
    assert equal_as_arrays(grader.constants['I'], identity(4))
Beispiel #15
0
def test_wrong_answer_type_error_messages():
    # Note: our convention is that single-row matrix and vectors cannot
    # be compared, [[1, 2, 3]] are graded as different [1, 2, 3]
    # (and, in particular, incomparable)

    grader = MatrixGrader(answers='[[1, 2, 3]]',
                          max_array_dim=2,
                          answer_shape_mismatch=dict(is_raised=True,
                                                     msg_detail='shape'))
    match = (r'Expected answer to be a matrix of shape \(rows: 1, cols: 3\), '
             r'but input is a vector of length 3')
    with raises(InputTypeError, match=match):
        grader(None, '[1, 2, 3]')

    grader = MatrixGrader(answers='[[1, 2, 3]]',
                          max_array_dim=2,
                          answer_shape_mismatch=dict(is_raised=True,
                                                     msg_detail='type'))
    match = 'Expected answer to be a matrix, but input is a vector'
    with raises(InputTypeError, match=match):
        grader(None, '[1, 2, 3]')

    grader = MatrixGrader(answers='[[1, 2, 3]]',
                          max_array_dim=2,
                          answer_shape_mismatch=dict(is_raised=False,
                                                     msg_detail='shape'))
    msg = ('Expected answer to be a matrix of shape (rows: 1, cols: 3), '
           'but input is a vector of length 3')
    assert grader(None, '[1, 2, 3]') == {
        'ok': False,
        'grade_decimal': 0,
        'msg': msg
    }

    grader = MatrixGrader(answers='[[1, 2, 3]]',
                          max_array_dim=2,
                          answer_shape_mismatch=dict(is_raised=False,
                                                     msg_detail='type'))
    msg = 'Expected answer to be a matrix, but input is a vector'
    assert grader(None, '[1, 2, 3]') == {
        'ok': False,
        'grade_decimal': 0,
        'msg': msg
    }

    msg = 'Expected answer to be a matrix, but input is a matrix of incorrect shape'
    assert grader(None, '[[1, 2, 3, 4]]') == {
        'ok': False,
        'grade_decimal': 0,
        'msg': msg
    }

    grader = MatrixGrader(answers='[[1, 2, 3]]',
                          max_array_dim=2,
                          answer_shape_mismatch=dict(is_raised=False,
                                                     msg_detail=None))
    assert grader(None, '[1, 2, 3]') == {
        'ok': False,
        'grade_decimal': 0,
        'msg': ''
    }
Beispiel #16
0
def test_matrix_inverses_work_if_not_disabled():
    grader = MatrixGrader(answers='A^2',
                          variables=['A'],
                          sample_from={'A': RealMatrices()})
    assert grader(None, 'A^3*A^-1')['ok']