コード例 #1
0
    def get_limits_and_funcs(integrand_str, lower_str, upper_str,
                             integration_var, varscope, funcscope):
        """Evals lower/upper limits and gets the functions used in lower/upper/integrand"""

        lower, lower_used = evaluator(lower_str,
                                      variables=varscope,
                                      functions=funcscope,
                                      suffixes={},
                                      allow_inf=True)
        upper, upper_used = evaluator(upper_str,
                                      variables=varscope,
                                      functions=funcscope,
                                      suffixes={},
                                      allow_inf=True)

        varscope[integration_var] = (upper + lower) / 2
        _, integrand_used = evaluator(integrand_str,
                                      variables=varscope,
                                      functions=funcscope,
                                      suffixes={},
                                      allow_inf=True)

        used_funcs = lower_used.functions_used.union(
            upper_used.functions_used, integrand_used.functions_used)

        return lower, upper, used_funcs
コード例 #2
0
def test_calcpy():
    """Tests of calc.py that aren't covered elsewhere"""

    # Test unhandled exception
    def badfunc(a):
        raise ValueError("Badness!")

    with raises(
            CalcError,
            match=
            r"There was an error evaluating f\(...\). Its input does not seem to be in its domain."
    ):
        evaluator("1+f(2)", {}, {"f": badfunc}, {})

    # Test formula with None
    result = evaluator(None, {}, {}, {})
    assert result[0] == approx(float('nan'), nan_ok=True)
    assert result[1] == set()

    # Test formulae with parallel operator
    result = evaluator("1 || 1 || 1", {}, {}, {})
    assert result[0] == 1 / 3
    assert result[1] == set()

    result = evaluator("1 || 1 || 0", {}, {}, {})
    assert result[0] == approx(float('nan'), nan_ok=True)
    assert result[1] == set()
コード例 #3
0
    def raw_check(self, answer, student_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'])

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

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

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

            # Compute expressions
            expected, _ = evaluator(formula=answer['expect'],
                                    case_sensitive=self.config['case_sensitive'],
                                    variables=varlist,
                                    functions=funclist,
                                    suffixes=self.suffixes)

            student, used_funcs = evaluator(student_input,
                                            case_sensitive=self.config['case_sensitive'],
                                            variables=varlist,
                                            functions=funclist,
                                            suffixes=self.suffixes)

            # Check that the required functions are used
            # But only the first time!
            if i == 0:
                for f in self.config["required_functions"]:
                    ftest = f
                    if not self.config['case_sensitive']:
                        ftest = f.lower()
                        used_funcs = [x.lower() for x in used_funcs]
                    if ftest not in used_funcs:
                        msg = "Invalid Input: Answer must contain the function {}"
                        raise InvalidInput(msg.format(f))

            # Check if expressions agree
            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': ''}

        # This response appears to agree with the expected answer
        return {
            'ok': answer['ok'],
            'grade_decimal': answer['grade_decimal'],
            'msg': answer['msg']
        }
コード例 #4
0
 def scoped_eval(expression,
                 variables=varlist,
                 functions=funclist,
                 suffixes=self.suffixes,
                 max_array_dim=self.config['max_array_dim']):
     return evaluator(expression, variables, functions, suffixes,
                      max_array_dim)
コード例 #5
0
 def raw_integrand(x):
     varscope[integration_var] = x
     value, _ = evaluator(integrand_str,
                          variables=varscope,
                          functions=funcscope,
                          suffixes={})
     return value
コード例 #6
0
 def raw_integrand(x):
     varscope[integration_var] = x
     value, _ = evaluator(integrand_str,
                          case_sensitive=self.config['case_sensitive'],
                          variables=varscope,
                          functions=funcscope,
                          suffixes={})
     return value
コード例 #7
0
def test_varnames():
    """Test variable names in calc.py"""
    # Tensor variable names
    assert evaluator("U^{ijk}", {"U^{ijk}": 2}, {}, {})[0] == 2
    assert evaluator("U_{ijk}/2", {"U_{ijk}": 2}, {}, {})[0] == 1
    assert evaluator("U_{ijk}^{123}", {"U_{ijk}^{123}": 2}, {}, {})[0] == 2
    assert evaluator("U_{ijk}^{123}'''''", {"U_{ijk}^{123}'''''": 2}, {},
                     {})[0] == 2
    assert evaluator("U_{ijk}^2", {"U_{ijk}": 2}, {}, {})[0] == 4
    assert evaluator("U^{ijk}^2", {"U^{ijk}": 2}, {}, {})[0] == 4
    assert evaluator("U_{ijk}^{123}^2", {"U_{ijk}^{123}": 2}, {}, {})[0] == 4
    # Regular variable names
    assert evaluator("U_cat/2 + Th3_dog__7a_", {
        "U_cat": 2,
        "Th3_dog__7a_": 4
    }, {}, {})[0] == 5
    # tensor subscripts need braces
    with raises(UnableToParse):
        assert evaluator("U_123^{ijk}", {}, {}, {})
    with raises(UnableToParse):
        assert evaluator("T_1_{123}^{ijk}", {}, {}, {})
コード例 #8
0
 def eval_summand(x):
     """
     Helper function to evaluate the summand at the given value of the
     summation variable.
     """
     varscope[summation_var] = x
     value, _ = evaluator(summand_str,
                          variables=varscope,
                          functions=funcscope,
                          suffixes=self.suffixes)
     del varscope[summation_var]
     return value
コード例 #9
0
    def get_limits_and_funcs(self, expression, lower_str, upper_str, varscope,
                             funcscope):
        """
        Evals lower/upper limits and gets the functions used in limits and integrand/summand.
        """
        lower, lower_used = evaluator(lower_str,
                                      variables=varscope,
                                      functions=funcscope,
                                      suffixes=self.suffixes,
                                      allow_inf=True)
        upper, upper_used = evaluator(upper_str,
                                      variables=varscope,
                                      functions=funcscope,
                                      suffixes=self.suffixes,
                                      allow_inf=True)
        expression_used = parse(expression)

        used_funcs = lower_used.functions_used.union(
            upper_used.functions_used, expression_used.functions_used)

        return lower, upper, used_funcs
コード例 #10
0
    def compute_sample(self, sample_dict, functions, suffixes):
        """Compute the value of this sample"""
        try:
            result, _ = evaluator(formula=self.config['formula'],
                                  variables=sample_dict,
                                  functions=functions,
                                  suffixes=suffixes)
        except CalcError:
            raise ConfigError("Formula error in dependent sampling formula: " +
                              self.config["formula"])

        return result
コード例 #11
0
    def compute_sample(self, sample_dict):
        """Compute the value of this sample"""
        try:
            result, _ = evaluator(formula=self.config['formula'],
                                  case_sensitive=self.config['case_sensitive'],
                                  variables=sample_dict,
                                  functions=DEFAULT_FUNCTIONS,
                                  suffixes=DEFAULT_SUFFIXES)
        except CalcError:
            raise ConfigError("Formula error in dependent sampling formula: " +
                              self.config["formula"])

        return result
コード例 #12
0
    def evaluate_int(self,
                     integrand_str,
                     lower_str,
                     upper_str,
                     integration_var,
                     varscope=None,
                     funcscope=None):
        varscope = {} if varscope is None else varscope
        funcscope = {} if funcscope is None else funcscope

        lower, _ = evaluator(lower_str,
                             case_sensitive=self.config['case_sensitive'],
                             variables=varscope,
                             functions=funcscope,
                             suffixes={})
        upper, _ = evaluator(upper_str,
                             case_sensitive=self.config['case_sensitive'],
                             variables=varscope,
                             functions=funcscope,
                             suffixes={})

        if isinstance(lower, complex) or isinstance(upper, complex):
            raise IntegrationError(
                'Integration limits must be real but have evaluated to complex numbers.'
            )

        # It is possible that the integration variable might appear in the limits.
        # Some consider this bad practice, but many students do it and Mathematica allows it.
        # We're going to edit the varscope below to contain the integration variable.
        # Let's store the integration variable's initial value in case it has one.
        int_var_initial = varscope[
            integration_var] if integration_var in varscope else None

        def raw_integrand(x):
            varscope[integration_var] = x
            value, _ = evaluator(integrand_str,
                                 case_sensitive=self.config['case_sensitive'],
                                 variables=varscope,
                                 functions=funcscope,
                                 suffixes={})
            return value

        if self.config['complex_integrand']:
            integrand_re = lambda x: real(raw_integrand(x))
            integrand_im = lambda x: imag(raw_integrand(x))
            result_re = integrate.quad(integrand_re, lower, upper,
                                       **self.config['integrator_options'])
            result_im = integrate.quad(integrand_im, lower, upper,
                                       **self.config['integrator_options'])
        else:
            errmsg = "Integrand has evaluated to complex number but must evaluate to a real."
            integrand = check_output_is_real(raw_integrand, IntegrationError,
                                             errmsg)
            result_re = integrate.quad(integrand, lower, upper,
                                       **self.config['integrator_options'])
            result_im = (None, None, {'neval': None})

        # Restore the integration variable's initial value now that we are done integrating
        if int_var_initial is not None:
            varscope[integration_var] = int_var_initial

        return result_re, result_im
コード例 #13
0
 def cos_summand(n):
     value, _ = evaluator('(-1)^(n/2)*x^n/fact(n)', {'n': n, 'x': x})
     return value
コード例 #14
0
 def sin_summand(n):
     value, _ = evaluator('(-1)^((n-1)/2)*x^n/fact(n)', {'n': n, 'x': x})
     return value
コード例 #15
0
 def exp_summand(n):
     value, _ = evaluator('x^n/fact(n)', {'n': n, 'x': x})
     return value