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 = 'Упростите {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='Пусть {function} = {unsimplified}.', function=function, unsimplified=unsimplified)
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='Пусть {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='Пусть {self} {composed}.', composed=composed)
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}?', # ] templates = [ '{p}' + ops.MUL_SYMBOL + '{q}', '{p} ' + ops.MUL_SYMBOL + ' {q}', 'Вычислите {p}' + ops.MUL_SYMBOL + '{q}.', 'Рассчитайте {p} ' + ops.MUL_SYMBOL + ' {q}.', 'Умножьте {p} и {q}.', 'Чему равно произведение {p} и {q}?', 'Умножьте {p} на {q}.', 'Сколько будет {p} умножить на {q}?', 'Каков результат произведения {p} и {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='Пусть {self} = {p} * {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}?', # ]) template = random.choice([ # '{op}', 'Чему равно {op}?', 'Решите {op}.', 'Вычислите {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='Пусть {self} равняется {op}.', op=op)
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(['наибольший', 'самый большой']) + ' общий ' + random.choice(['знаменатель'])) if is_question: template = random.choice([ 'Расчитайте {adjective} {p} и {q}.', 'Чему равен {adjective} {p} и {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='Пусть {self} - это {adjective} {p} и {q}.', adjective=adjective, p=p, q=q)
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}?', # ]) template = random.choice([ '{p} + {q}', '{p}+{q}', 'Рассчитайте {p} + {q}.', 'Сложите {p} и {q}.', 'Прибавьте {p} к {q}.', 'Найдите сумму {p} и {q}.', 'Чему равна сумма {p} и {q}.', 'Сколько получится, если сложить {p} и {q}.', 'Сколько будет {p} плюс {q}?', 'Вычислите {p} + {q}.', 'Чему равно {p} + {q}?', ]) return example.Problem( question=example.question(context, template, p=p, q=q), answer=value) else: return composition.Entity( context=context, value=value, description='Пусть {self} = {p} + {q}.', p=p, q=q)
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}?', # ] templates = [ '{p} - {q}', 'Рассчитайте {p} - {q}.', 'Сколько будет {p} минус {q}?', 'Сколько будет {p} отнять {q}?', 'Какое число на {q} меньше {p}?', 'Отнимите {q} от {p}.', 'Вычислите {p} - {q}.', 'Чему равно {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 ['разница']: for pair in ['{p} и {q}', '{q} и {p}']: templates.append('Чему равна {} {}?'.format(adjective, pair)) template = random.choice(templates) return example.Problem( question=example.question(context, template, p=p, q=q), answer=value) else: return composition.Entity( context=context, value=value, description='Пусть {self} = {p} - {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([ 'Разделите {p} на {q}.', 'Чему равно {p} разделить на {q}', 'Сколько получится, если {p} поделить на {q}?', 'Чему равен результат деления {p} на {q}.', ]) return example.Problem( question=example.question(context, template, p=p, q=q), answer=answer ) else: return composition.Entity( context=context, value=answer, description='Пусть {self} равно {p} разделить на {q}.', 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([ 'Расчитайте остаток от деления {p} на {q}.', 'Чему равен остаток деления {p} на {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='Пусть {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 _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) nth_fem = display.StringOrdinal_fem(derivative_order) nth_fem_gen = display.StringOrdinal_fem_gen(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, nth_fem=nth_fem, nth_fem_gen=nth_fem_gen), 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= 'Пусть {fn}({variables}) - это {nth_fem} производная функции {eq}.', handle=composition.FunctionHandle(fn_symbol), fn=fn_symbol, variables=variables_string, nth=nth, nth_fem=nth_fem, nth_fem_gen=nth_fem_gen, eq=polynomial)