def compose(value, sample_args, context=None): """E.g., "Let f(x)=2x+1, let g(x)=3x+10. What is f(g(x))?".""" del value # unused if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() entropy_f, entropy_g = entropy * np.random.dirichlet([1, 1]) coeffs_f = polynomials.sample_coefficients([random.randint(1, 2)], entropy_f) coeffs_g = polynomials.sample_coefficients([random.randint(1, 2)], entropy_g) entity_f, entity_g = context.sample( sample_args, [composition.Polynomial(coeffs_f), composition.Polynomial(coeffs_g)]) variable = sympy.var(context.pop()) poly_f = polynomials.coefficients_to_polynomial(coeffs_f, variable) poly_g = polynomials.coefficients_to_polynomial(coeffs_g, variable) poly_f_g = poly_f.sympy().subs(variable, poly_g.sympy()).expand() expression = composition.FunctionHandle(entity_f, entity_g).apply(variable) template = random.choice(_TEMPLATES) return example.Problem(question=example.question(context, template, composed=expression), answer=poly_f_g)
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 _generate_polynomial(num_variables, entropy, derivative_order, derivative_axis): """Returns polynomial.""" # Note: numpy randint has upper bound as ) not ], unlike python random.randint degrees = np.random.randint(1, 4, [num_variables]) degrees[derivative_axis] = np.random.randint(0, 4) # allow to be zero here. coefficients = polynomials.sample_coefficients(degrees, entropy) # We also generate coefficients that will disappear when differentiated. # Thus we don't account for the entropy used here. assert derivative_order > 0 degrees[derivative_axis] = derivative_order - 1 extra_coefficients = polynomials.sample_coefficients(degrees, entropy) return np.concatenate([extra_coefficients, coefficients], axis=derivative_axis)
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 coefficient_named(value, sample_args, context=None): """E.g., "Express x^2 + 2x in the form h * x^2 + k * x + t and give h.".""" del value # not used if context is None: context = composition.Context() variable = sympy.Symbol(context.pop()) entropy, sample_args = sample_args.peel() degree = random.randint(1, 4) if random.choice([False, True]): coefficients = polynomials.sample_coefficients( degree, entropy / 2, min_non_zero=random.randint(degree - 1, degree)) expanded = polynomials.expand_coefficients(coefficients, entropy / 2) expression = polynomials.coefficients_to_polynomial(expanded, variable) else: expression = polynomials.sample_with_brackets(variable, degree, entropy) coefficients = list(reversed(sympy.Poly(expression).all_coeffs())) named_coeffs = [sympy.Symbol(context.pop()) for _ in range(degree + 1)] canonical = polynomials.coefficients_to_polynomial(named_coeffs, variable) if random.random() < 0.2: # only small probability of non-zero power power = random.randint(0, degree) else: non_zero_powers = [ i for i in range(degree + 1) if coefficients[i] != 0 ] power = random.choice(non_zero_powers) value = coefficients[power] named_coeff = named_coeffs[power] template = random.choice([ 'Выразите {expression} в форме {canonical} и найдите {target}.', 'Преобразуйте {expression} в форму {canonical} и найдите {target}.', # 'Express {expression} in the form {canonical} and give {target}.', # 'Rearrange {expression} to the form {canonical} and give {target}.', ]) return example.Problem(question=example.question(context, template, expression=expression, canonical=canonical, target=named_coeff), answer=value)
def _sample_integrand(coefficients, derivative_order, derivative_axis, entropy): """Integrates `coefficients` and adds sampled "constant" terms.""" coefficients = np.asarray(coefficients) # Integrate (with zero for constant terms). integrand = coefficients for _ in range(derivative_order): integrand = polynomials.integrate(integrand, derivative_axis) # Add on sampled constant terms. constant_degrees = np.array(integrand.shape) - 1 constant_degrees[derivative_axis] = derivative_order - 1 extra_coeffs = polynomials.sample_coefficients(constant_degrees, entropy) pad_amount = coefficients.shape[derivative_axis] pad = [(0, pad_amount if i == derivative_axis else 0) for i in range(coefficients.ndim)] extra_coeffs = np.pad(extra_coeffs, pad, 'constant', constant_values=0) return integrand + extra_coeffs