def _sub_question_or_entity(context, p, q, is_question): """Generates entity or question for subtraction p - q.""" value = p.value - q.value if is_question: templates = [ '{p} - {q}', 'Work out {p} - {q}.', 'What is {p} minus {q}?', 'What is {p} take away {q}?', 'What is {q} less than {p}?', 'Subtract {q} from {p}.', 'Calculate {p} - {q}.', 'What is {p} - {q}?', ] if sympy.Ge(p.value, q.value): # We calculate p - q, so the difference (|p - q|) is the correct answer. for adjective in ['distance', 'difference']: for pair in ['{p} and {q}', '{q} and {p}']: templates.append('What is the {} between {}?'.format(adjective, pair)) template = random.choice(templates) return example.Problem( question=example.question(context, template, p=p, q=q, problem_type='sub'), answer=value) else: return composition.Entity( context=context, value=value, description='Let {self} = {p} - {q}.', p=p, q=q, problem_type='sub')
def _add_question_or_entity(context, p, q, is_question): """Generates entity or question for adding p + q.""" value = p.value + q.value if is_question: template = random.choice([ '{p} + {q}', '{p}+{q}', 'Work out {p} + {q}.', 'Add {p} and {q}.', 'Put together {p} and {q}.', 'Sum {p} and {q}.', 'Total of {p} and {q}.', 'Add together {p} and {q}.', 'What is {p} plus {q}?', 'Calculate {p} + {q}.', 'What is {p} + {q}?', ]) return example.Problem( question=example.question(context, template, p=p, q=q, problem_type='add'), answer=value) else: return composition.Entity( context=context, value=value, description='Let {self} = {p} + {q}.', p=p, q=q, problem_type='add')
def collect(value, sample_args, context=None): """Collect terms in an unsimplified polynomial.""" is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() if value is None: entropy_value, entropy = entropy * np.random.dirichlet([2, 3]) degrees = [random.randint(1, 3)] value = composition.Polynomial( polynomials.sample_coefficients(degrees, entropy_value)) assert isinstance(value, composition.Polynomial) coefficients = value.coefficients all_coefficients_are_integer = True for coeff in coefficients.flat: if not number.is_integer(coeff): all_coefficients_are_integer = False break if all_coefficients_are_integer: coefficients = polynomials.expand_coefficients(coefficients, entropy) else: # put back the unused entropy sample_args = composition.SampleArgs(sample_args.num_modules, sample_args.entropy + entropy) num_variables = coefficients.ndim variables = [sympy.Symbol(context.pop()) for _ in range(num_variables)] unsimplified = polynomials.coefficients_to_polynomial( coefficients, variables) simplified = unsimplified.sympy().expand() # Bit of a hack: handle the very rare case where no number constants appearing if not ops.number_constants(unsimplified): unsimplified = ops.Add(unsimplified, ops.Constant(0)) context.sample_by_replacing_constants(sample_args, unsimplified) if is_question: template = 'Sederhanakan {unsimplified}.' return example.Problem(question=example.question( context, template, unsimplified=unsimplified), answer=simplified) else: function_symbol = context.pop() function = sympy.Function(function_symbol)(*variables) return composition.Entity( context=context, value=value, handle=composition.FunctionHandle(function_symbol), expression=unsimplified, polynomial_variables=variables, description='Misalkan {function} = {unsimplified}.', function=function, unsimplified=unsimplified)
def _differentiate_polynomial(value, sample_args, context, num_variables): """Generates a question for differentiating a polynomial.""" is_question = context is None if context is None: context = composition.Context() if value is not None: num_variables = value.coefficients.ndim entropy, sample_args = sample_args.peel() max_derivative_order = 3 derivative_order = random.randint(1, max_derivative_order) entropy = max(0, entropy - math.log10(max_derivative_order)) derivative_axis = random.randint(0, num_variables - 1) if value is None: coefficients = _generate_polynomial( num_variables, entropy, derivative_order, derivative_axis) else: coefficients = _sample_integrand( value.coefficients, derivative_order, derivative_axis, entropy) (entity,) = context.sample( sample_args, [composition.Polynomial(coefficients)]) value = coefficients for _ in range(derivative_order): value = polynomials.differentiate(value, axis=derivative_axis) nth = display.StringOrdinal(derivative_order) if entity.has_expression(): polynomial = entity.expression variables = entity.polynomial_variables else: variables = [sympy.Symbol(context.pop()) for _ in range(num_variables)] polynomial = entity.handle.apply(*variables) variable = variables[derivative_axis] if is_question: template = _template(context.module_count, derivative_order, len(variables)) answer = polynomials.coefficients_to_polynomial(value, variables).sympy() return example.Problem( question=example.question( context, template, eq=polynomial, var=variable, nth=nth), answer=answer) else: fn_symbol = context.pop() variables_string = ', '.join(str(variable) for variable in variables) assert len(variables) == 1 # since below we don't specify var we diff wrt return composition.Entity( context=context, value=composition.Polynomial(value), description='Misalkan {fn} ({variables}) menjadi turunan ke-{nth} dari {eq}.', handle=composition.FunctionHandle(fn_symbol), fn=fn_symbol, variables=variables_string, nth=nth, eq=polynomial)
def add(value, sample_args, context=None): """E.g., "Let f(x)=2x+1, g(x)=3x+2. What is 5*f(x) - 7*g(x)?".""" is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() if value is None: max_degree = 3 degree = random.randint(1, max_degree) entropy -= math.log10(max_degree) entropy_value = entropy / 2 entropy -= entropy_value value = polynomials.sample_coefficients(degree, entropy=entropy_value, min_non_zero=random.randint( 1, 3)) value = composition.Polynomial(value) c1, c2, coeffs1, coeffs2 = polynomials.coefficients_linear_split( value.coefficients, entropy) coeffs1 = polynomials.trim(coeffs1) coeffs2 = polynomials.trim(coeffs2) c1, c2, fn1, fn2 = context.sample(sample_args, [ c1, c2, composition.Polynomial(coeffs1), composition.Polynomial(coeffs2) ]) var = sympy.var(context.pop()) expression = (c1.handle * fn1.handle.apply(var) + c2.handle * fn2.handle.apply(var)) if is_question: answer = polynomials.coefficients_to_polynomial( value.coefficients, var) answer = answer.sympy() template = random.choice(_TEMPLATES) return example.Problem(question=example.question(context, template, composed=expression), answer=answer) else: intermediate_symbol = context.pop() intermediate = sympy.Function(intermediate_symbol)(var) return composition.Entity( context=context, value=value, description='Misalkan {intermediate} = {composed}.', handle=composition.FunctionHandle(intermediate_symbol), intermediate=intermediate, composed=expression)
def evaluate(value, sample_args, context=None): """Entity for evaluating an integer-valued polynomial at a given point.""" is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() if value is None: entropy_value = random.uniform(1, 1 + entropy / 3) entropy = max(0, entropy - entropy_value) value = number.integer(entropy_value, signed=True) entropy_input = random.uniform(1, 1 + entropy / 3) entropy = max(0, entropy - entropy_input) input_ = number.integer(entropy_input, signed=True) degree = random.randint(1, 3) entropies = entropy * np.random.dirichlet(list(range(1, degree + 1))) # Calculate coefficients in reverse order. target = value coeffs_reversed = [] for i, coeff_entropy in enumerate(entropies): power = degree - i coeff = number.integer(coeff_entropy, signed=True) if input_ != 0: coeff += int(round(target / input_**power)) if coeff == 0 and i == 0: # Don't allow zero in leading coefficient. coeff += random.choice([-1, 1]) coeffs_reversed.append(coeff) target -= coeff * (input_**power) coeffs_reversed.append(target) coefficients = list(reversed(coeffs_reversed)) (polynomial_entity, input_) = context.sample(sample_args, [composition.Polynomial(coefficients), input_]) composed = polynomial_entity.handle.apply(input_.handle) if is_question: template = random.choice(_TEMPLATES) return example.Problem(question=example.question(context, template, composed=composed), answer=value) else: return composition.Entity(context=context, value=value, expression=composed, description='Misalkan {self} be {composed}.', composed=composed)
def gcd(value, sample_args, context=None): """Question for greatest common divisor of p and q.""" is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() if value is None: value_entropy = 1 + random.uniform(0, entropy / 3) entropy = max(1, entropy - value_entropy) value = number.integer(value_entropy, False, min_abs=1) p_mult, q_mult = _random_coprime_pair(entropy) p = value * p_mult q = value * q_mult assert sympy.gcd(p, q) == value p, q = context.sample(sample_args, [p, q]) adjective = (random.choice(['pembagi persekutuan', 'faktor umum']) + random.choice([' terbesar', ' tertinggi'])) if is_question: template = random.choice([ 'Hitung {adjective} dari {p} dan {q}.', 'Apa {adjective} dari {p} dan {q}?', ]) return example.Problem(question=example.question(context, template, adjective=adjective, p=p, q=q), answer=value) else: return composition.Entity( context=context, value=value, description='Biarkan {self} menjadi {adjective} dari {p} dan {q}.', adjective=adjective, p=p, q=q)
def gcd(value, sample_args, context=None): """Question for greatest common divisor of p and q.""" is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() if value is None: value_entropy = 1 + random.uniform(0, entropy / 3) entropy = max(1, entropy - value_entropy) value = number.integer(value_entropy, False, min_abs=1) p_mult, q_mult = _random_coprime_pair(entropy) p = value * p_mult q = value * q_mult assert sympy.gcd(p, q) == value p, q = context.sample(sample_args, [p, q]) adjective = (random.choice(['greatest', 'highest']) + ' common ' + random.choice(['divisor', 'factor'])) if is_question: template = random.choice([ 'Calculate the {adjective} of {p} and {q}.', 'What is the {adjective} of {p} and {q}?', ]) return example.Problem(question=example.question(context, template, adjective=adjective, p=p, q=q), answer=value) else: return composition.Entity( context=context, value=value, description='Let {self} be the {adjective} of {p} and {q}.', adjective=adjective, p=p, q=q)
def div_remainder(value, sample_args, context=None): """E.g., "What is the remainder when 27 is divided by 5?".""" is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() if value is None: entropy_value = 1 + random.uniform(0, entropy / 3) entropy = max(0, entropy - entropy_value) value = number.integer(entropy_value, signed=False) entropy_a, entropy_q = entropy * np.random.dirichlet([1, 1]) a = number.integer(entropy_a, signed=False, min_abs=1) q = value + number.integer(entropy_q, signed=False, min_abs=1) p = a * q + value assert p % q == value p, q = context.sample(sample_args, [p, q]) if is_question: template = random.choice([ 'Hitung sisanya ketika {p} dibagi dengan {q}.', 'Berapa sisa jika {p} dibagi dengan {q}?', ]) return example.Problem(question=example.question( context, template, p=p.expression_else_handle, q=q.expression_else_handle), answer=value) else: return composition.Entity( context=context, value=value, description= 'Biarkan {self} menjadi sisa saat {p} dibagi dengan {q}.', p=p, q=q)
def div(value, sample_args, context=None): """Returns random question for dividing two numbers.""" del value # unused is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() entropy_1, entropy_q = _entropy_for_pair(entropy) q = number.integer(entropy_q, True, min_abs=1) if random.choice([False, True]): # Pick p/q with nice integer result. answer = number.integer(entropy_1, True) p = answer * q else: p = number.integer(entropy_1, True) answer = p / q p, q = context.sample(sample_args, [p, q]) if is_question: template = random.choice([ 'Divide {p} by {q}.', '{p} divided by {q}', 'What is {p} divided by {q}?', 'Calculate {p} divided by {q}.', ]) return example.Problem( question=example.question(context, template, p=p, q=q), answer=answer ) else: return composition.Entity( context=context, value=answer, description='Let {self} be {p} divided by {q}.', p=p, q=q)
def _calculate(value, sample_args, context, add_sub, mul_div, length=None): """Questions for evaluating arithmetic expressions.""" is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() if value in [_INT, _INT_OR_RATIONAL]: value_entropy = max(1.0, entropy / 4) entropy = max(1.0, entropy - value_entropy) sampler = _value_sampler(value) value = sampler(value_entropy) op = arithmetic.arithmetic( value=value, entropy=entropy, add_sub=add_sub, mul_div=mul_div, length=length) context.sample_by_replacing_constants(sample_args, op) if is_question: template = random.choice([ '{op}', 'What is {op}?', 'Evaluate {op}.', 'Calculate {op}.', 'What is the value of {op}?', ]) return example.Problem( question=example.question(context, template, op=op), answer=value) else: return composition.Entity( context=context, value=value, expression=op, description='Let {self} be {op}.', op=op)
def mul(value, sample_args, context=None): """Returns random question for multiplying two numbers.""" del value # unused is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() entropy_p, entropy_q = _entropy_for_pair(entropy) p = number.integer_or_decimal(entropy_p, True) q = number.integer_or_decimal(entropy_q, True) p, q = context.sample(sample_args, [p, q]) answer = p.value * q.value if is_question: templates = [ '{p}' + ops.MUL_SYMBOL + '{q}', '{p} ' + ops.MUL_SYMBOL + ' {q}', 'Calculate {p}' + ops.MUL_SYMBOL + '{q}.', 'Work out {p} ' + ops.MUL_SYMBOL + ' {q}.', 'Multiply {p} and {q}.', 'Product of {p} and {q}.', 'What is the product of {p} and {q}?', '{p} times {q}', 'What is {p} times {q}?', ] template = random.choice(templates) return example.Problem( question=example.question(context, template, p=p, q=q), answer=answer ) else: return composition.Entity( context=context, value=answer, description='Let {self} = {p} * {q}.', p=p, q=q)
def testInit_valueErrorIfSelfAndHandle(self): with self.assertRaisesRegexp(ValueError, 'Cannot specify handle'): composition.Entity(context=composition.Context(), value=0, description='Something with {self}. ', handle='additional')
def _solve_linear_system(degree, value, sample_args, context=None): """Solve linear equations.""" is_question = context is None if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() solutions = [] if value is not None: solutions.append(value) extra_solutions_needed = degree - len(solutions) if extra_solutions_needed > 0: entropies = (entropy / 4) * np.random.dirichlet( np.ones(extra_solutions_needed)) entropies = np.maximum(1, entropies) # min per-solution entropy entropy -= sum(entropies) solutions += [ number.integer(solution_entropy, True) for solution_entropy in entropies ] entropy = max(1, entropy) variables = [sympy.Symbol(context.pop()) for _ in range(degree)] solution_index = 0 # If we're going to be creating a linear system with constants to replace by # handles from other modules, then we need a linear system with constants # occurring. Very occasionally this can fail to happen, e.g., "x = -x"; # normally this while loop will only see one iteration. while True: equations = linear_system.linear_system(variables=variables, solutions=solutions, entropy=entropy, non_trivial_in=solution_index) constants = ops.number_constants(equations) if sample_args.num_modules <= 1 or constants: break context.sample_by_replacing_constants(sample_args, equations) variable = variables[solution_index] answer = solutions[solution_index] equations = ', '.join([str(equation) for equation in equations]) if is_question: template = random.choice([ 'Selesaikan {equations} untuk {variable}.', ]) return example.Problem( example.question(context, template, equations=equations, variable=variable), answer) else: return composition.Entity(context=context, value=answer, description='Misalkan {equations}.', handle=variable, equations=equations)