def test_orthogonal():
    # Test shape, orthogonality, determinant, python2 error, MathArray
    if six.PY2:
        with raises(NotImplementedError, match='This feature requires newer versions of '
                                               'numpy and scipy than are available.'):
            matrices = OrthogonalMatrices(unitdet=False)
            matrices.gen_sample()
        with raises(NotImplementedError, match='This feature requires newer versions of '
                                               'numpy and scipy than are available.'):
            matrices = OrthogonalMatrices(unitdet=True)
            matrices.gen_sample()
    else:
        # These are the doctests
        matrices = OrthogonalMatrices()
        assert matrices.gen_sample().shape == (2, 2)

        matrices = OrthogonalMatrices(dimension=4)
        assert matrices.gen_sample().shape == (4, 4)

        matrices = OrthogonalMatrices(unitdet=True)
        assert within_tolerance(np.linalg.det(matrices.gen_sample()), 1, 1e-14)

        matrices = OrthogonalMatrices(unitdet=False)
        m = matrices.gen_sample()
        assert (within_tolerance(np.linalg.det(m), 1, 1e-14)
                or within_tolerance(np.linalg.det(m), -1, 1e-14))

        matrices = OrthogonalMatrices(unitdet=True)
        m = matrices.gen_sample()
        assert within_tolerance(m * np.conjugate(np.transpose(m)), MathArray(np.eye(2)), 1e-14)

        matrices = OrthogonalMatrices(unitdet=False)
        m = matrices.gen_sample()
        assert within_tolerance(m * np.conjugate(np.transpose(m)), MathArray(np.eye(2)), 1e-14)

        shapes = tuple(range(2, 5))
        dets = (True, False)

        # More general testing
        for shape in shapes:
            for det in dets:
                matrices = matrices = OrthogonalMatrices(dimension=shape, unitdet=det)
                m = matrices.gen_sample()
                assert m.shape == (shape, shape)
                assert np.array_equal(np.conj(m), m)
                assert within_tolerance(m * np.transpose(m), MathArray(np.eye(shape)), 1e-14)
                assert (approx(np.abs(np.linalg.det(m)), 1)
                        or approx(np.abs(np.linalg.det(m)), -1))
                if det:
                    assert approx(np.linalg.det(m), 1)
                assert isinstance(m, MathArray)
def test_general_matrices():
    # Test shape, real/complex, norm, triangular options, MathArray
    shapes = product(tuple(range(2, 5)), tuple(range(2, 5)))
    norms = ([2, 6], [3, 4], [4, 12], [1 - 1e-12, 1 + 1e-12])
    triangles = (None, 'upper', 'lower')

    for shape in shapes:
        for norm in norms:
            for triangle in triangles:
                matrices = RealMatrices(shape=shape, norm=norm, triangular=triangle)
                m = matrices.gen_sample()
                assert m.shape == shape
                assert norm[0] <= np.linalg.norm(m) <= norm[1]
                assert np.array_equal(np.conj(m), m)
                assert isinstance(m, MathArray)
                if triangle is None:
                    assert not within_tolerance(m, MathArray(np.triu(m)), 0)
                    assert not within_tolerance(m, MathArray(np.tril(m)), 0)
                elif triangle == "upper":
                    assert within_tolerance(m, MathArray(np.triu(m)), 0)
                elif triangle == "lower":
                    assert within_tolerance(m, MathArray(np.tril(m)), 0)

                matrices = ComplexMatrices(shape=shape, norm=norm, triangular=triangle)
                m = matrices.gen_sample()
                assert m.shape == shape
                assert norm[0] <= np.linalg.norm(m) <= norm[1]
                assert not np.array_equal(np.conj(m), m)
                assert isinstance(m, MathArray)
                if triangle is None:
                    assert not within_tolerance(m, MathArray(np.triu(m)), 0)
                    assert not within_tolerance(m, MathArray(np.tril(m)), 0)
                elif triangle == "upper":
                    assert within_tolerance(m, MathArray(np.triu(m)), 0)
                elif triangle == "lower":
                    assert within_tolerance(m, MathArray(np.tril(m)), 0)
 def _within_tolerance(x, y):
     return within_tolerance(x, y, self.config['tolerance'])
Esempio n. 4
0
    def raw_check(self, answer, cleaned_input):
        """Perform the numerical check of student_input vs answer"""

        var_samples = gen_symbols_samples(self.config['variables'],
                                          self.config['samples'],
                                          self.config['sample_from'],
                                          self.functions, {})

        func_samples = gen_symbols_samples(self.random_funcs.keys(),
                                           self.config['samples'],
                                           self.random_funcs, self.functions,
                                           {})

        # Make a copy of the functions and variables lists
        # We'll add the sampled functions/variables in
        funcscope = self.functions.copy()
        varscope = self.constants.copy()

        num_failures = 0
        for i in range(self.config['samples']):
            # Update the functions and variables listings with this sample
            funcscope.update(func_samples[i])
            varscope.update(var_samples[i])

            # Evaluate integrals. Error handling here is in two parts because
            # 1. custom error messages we've added
            # 2. scipy's warnings re-raised as error messages
            try:
                expected_re, expected_im, _ = self.evaluate_int(
                    answer['integrand'],
                    answer['lower'],
                    answer['upper'],
                    answer['integration_variable'],
                    varscope=varscope,
                    funcscope=funcscope)
            except IntegrationError as e:
                msg = "Integration Error with author's stored answer: {}"
                raise ConfigError(msg.format(e.message))

            student_re, student_im, used_funcs = self.evaluate_int(
                cleaned_input['integrand'],
                cleaned_input['lower'],
                cleaned_input['upper'],
                cleaned_input['integration_variable'],
                varscope=varscope,
                funcscope=funcscope)

            # scipy raises integration warnings when things go wrong,
            # except they aren't really warnings, they're just printed to stdout
            # so we use quad's full_output option to catch the messages, and then raise errors.
            # The 4th component only exists when its warning message is non-empty
            if len(student_re) == 4:
                raise IntegrationError(student_re[3])
            if len(expected_re) == 4:
                raise ConfigError(expected_re[3])
            if len(student_im) == 4:
                raise IntegrationError(student_im[3])
            if len(expected_im) == 4:
                raise ConfigError(expected_im[3])

            self.log(
                self.debug_appendix_template.format(
                    samplenum=i,
                    variables=varscope,
                    student_re_eval=student_re[0],
                    student_re_error=student_re[1],
                    student_re_neval=student_re[2]['neval'],
                    student_im_eval=student_im[0],
                    student_im_error=student_im[1],
                    student_im_neval=student_im[2]['neval'],
                    author_re_eval=expected_re[0],
                    author_re_error=expected_re[1],
                    author_re_neval=expected_re[2]['neval'],
                    author_im_eval=expected_im[0],
                    author_im_error=expected_im[1],
                    author_im_neval=expected_im[2]['neval'],
                ))

            # Check if expressions agree
            expected = expected_re[0] + (expected_im[0] or 0) * 1j
            student = student_re[0] + (student_im[0] or 0) * 1j
            if not within_tolerance(expected, student,
                                    self.config['tolerance']):
                num_failures += 1
                if num_failures > self.config["failable_evals"]:
                    return {
                        'ok': False,
                        'grade_decimal': 0,
                        'msg': ''
                    }, used_funcs

        # This response appears to agree with the expected answer
        return {'ok': True, 'grade_decimal': 1, 'msg': ''}, used_funcs
def test_square_matrices():
    # Test shape, real/complex, norm, symmetry, traceless, det, MathArray
    shapes = tuple(range(2, 5))
    norms = ([2, 6], [1 - 1e-12, 1 + 1e-12])
    symmetries = (None, 'diagonal', 'symmetric', 'antisymmetric', 'hermitian', 'antihermitian')
    traceless_opts = (True, False)
    dets = (None, 0, 1)
    complexes = (True, False)

    for det, traceless, symmetry in product(dets, traceless_opts, symmetries):
        # Handle the cases that don't work
        if det == 0 and traceless:
            with raises(ConfigError, match='Unable to generate zero determinant traceless matrices'):
                SquareMatrices(traceless=traceless,
                               symmetry=symmetry,
                               determinant=det)
            continue

        # Continue with further cases
        for shape, norm, comp in product(shapes, norms, complexes):
            # Check for matrices that don't exist
            args = {'dimension': shape, 'norm': norm, 'traceless': traceless,
                    'symmetry': symmetry, 'determinant': det, 'complex': comp}
            if det == 1:
                if traceless and shape == 2:
                    if symmetry == 'diagonal' and not comp:
                        with raises(ConfigError, match='No real, traceless, unit-determinant, diagonal 2x2 matrix exists'):
                            SquareMatrices(**args)
                        continue
                    elif symmetry == 'symmetric' and not comp:
                        with raises(ConfigError, match='No real, traceless, unit-determinant, symmetric 2x2 matrix exists'):
                            SquareMatrices(**args)
                        continue
                    elif symmetry == 'hermitian':
                        with raises(ConfigError, match='No traceless, unit-determinant, Hermitian 2x2 matrix exists'):
                            SquareMatrices(**args)
                        continue
                elif shape % 2 == 1:  # Odd dimension
                    if symmetry == 'antisymmetric':
                        with raises(ConfigError, match='No unit-determinant antisymmetric matrix exists in odd dimensions'):
                            SquareMatrices(**args)
                        continue
                    elif symmetry == 'antihermitian':
                        with raises(ConfigError, match='No unit-determinant antihermitian matrix exists in odd dimensions'):
                            SquareMatrices(**args)
                        continue
            if det == 0 and symmetry == 'antisymmetric':
                if comp:
                    with raises(ConfigError, match='Unable to generate complex zero determinant antisymmetric matrices'):
                        SquareMatrices(**args)
                    continue
                if shape % 2 == 0:  # Even dimension
                    with raises(ConfigError, match='Unable to generate real zero determinant antisymmetric matrices in even dimensions'):
                        SquareMatrices(**args)
                    continue

            # Matrix exists, so let's test
            matrices = SquareMatrices(**args)
            if symmetry in ('hermitian', 'antihermitian'):
                comp = True
            m = matrices.gen_sample()

            # MathArray
            assert isinstance(m, MathArray)

            # Shape
            assert m.shape == (shape, shape)

            # Norm
            if det != 1:
                computed_norm = np.linalg.norm(m)
                assert norm[0] <= computed_norm or abs(computed_norm - norm[0]) < 1e-12
                assert computed_norm <= norm[1] or abs(computed_norm - norm[1]) < 1e-12

            # Complex
            if not comp:
                assert np.array_equal(np.conj(m), m)

            # Trace
            if traceless:
                assert within_tolerance(m.trace(), 0, 5e-13)

            # Determinant
            if det == 0:
                assert within_tolerance(np.abs(np.linalg.det(m)), 0, 1e-12)
            elif det == 1:
                assert within_tolerance(np.abs(np.linalg.det(m)), 1, 1e-12)

            # Symmetries
            if symmetry == 'diagonal':
                assert np.array_equal(np.diag(np.diag(m)), m)
            elif symmetry == 'symmetric':
                assert np.array_equal(m, m.T)
            elif symmetry == 'antisymmetric':
                assert np.array_equal(m, -m.T)
            elif symmetry == 'hermitian':
                assert np.array_equal(m, np.conj(m.T))
            elif symmetry == 'antihermitian':
                assert np.array_equal(m, -np.conj(m.T))