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 _value_sampler(value): """Returns sampler (e.g., number.integer) appropriate for `value`.""" if value == _INT or number.is_integer(value): return functools.partial(number.integer, signed=True) if value == _INT_OR_RATIONAL or isinstance(value, sympy.Rational): return functools.partial(number.integer_or_rational, signed=True) if isinstance(value, display.Decimal): return functools.partial(number.integer_or_decimal, signed=True) raise ValueError('Unrecognized value {} of type {}' .format(value, type(value)))
def integers_with_sum(value, count, entropy): """Returns list of integers with a given sum. Args: value: Target value. count: Integer >= 1; the number of integers to use. entropy: Entropy to use (in total). Returns: List of numbers summing to `value`. Raises: ValueError: If `value` is not an integer. """ # Special cases. if count == 0: assert value == 0 assert entropy == 0 return [] if count == 1: assert entropy == 0 return [value] if not number.is_integer(value): raise ValueError('value={} (type={}) is not an integer'.format( value, type(value))) # Because e.g., (1, 1) and (2, 2) will both map to the same set of integers # when we normalize to have sum equal to `value`. entropy *= count / (count - 1) min_term_entropy = max( 1, number.entropy_of_value(int(math.ceil(value / count)))) term_entropies = entropy * np.random.dirichlet(np.ones(count)) term_entropies = np.maximum(min_term_entropy, term_entropies) terms = [ number.integer(term_entropy, signed=True) for term_entropy in term_entropies ] delta = value - sum(terms) deltas = _split_value_equally(delta, count) terms = [term + delta for term, delta in zip(terms, deltas)] random.shuffle(terms) return terms
def round_number(value, sample_args, context=None): """Question for rounding integers and decimals.""" del value # unused for now if context is None: context = composition.Context() entropy, sample_args = sample_args.peel() # This is the power of 10 to round to. E.g., power == 0 corresponds to # rounding to the nearest integer; power == -2 corresponds to rounding to two # decimal places, and power == 3 corresponds to rounding to the nearest 1000. power = random.randint(-7, 6) answer_entropy = 1 + random.uniform(0, entropy / 2) entropy = max(1, entropy - answer_entropy) value_integer = number.integer(answer_entropy, signed=True) remainder_divisor = 10**int(math.ceil(entropy)) remainder_range_lower = -remainder_divisor / 2 remainder_range_upper = remainder_divisor / 2 if value_integer <= 0: remainder_range_lower += 1 if value_integer >= 0: remainder_range_upper -= 1 remainder = random.randint(remainder_range_lower, remainder_range_upper) input_ = value_integer + sympy.Rational(remainder, remainder_divisor) scale = 10**power if power >= 0 else sympy.Rational(1, 10**(-power)) input_ = input_ * scale value = value_integer * scale if not number.is_integer(input_): input_ = display.Decimal(input_) if not number.is_integer(value): value = display.Decimal(value) (input_, ) = context.sample(sample_args, [input_]) des = False if power > 0: # Rounding to a power of ten. round_to = 10**power if round_to in [100, 1000]: if random.choice([False, True]): # Write the rounding value as a word instead. if round_to == 10: des = True round_to = display.StringNumber( round_to, case='do', desyatok=des, join_number_words_with_hyphens=False, singular=True) description = 'ближайшей {round_to}'.format(round_to=round_to) elif round_to in [1000000, 1000000000]: if random.choice([False, True]): # Write the rounding value as a word instead. if round_to == 10: des = True round_to = display.StringNumber( round_to, case='do', desyatok=des, join_number_words_with_hyphens=False, singular=True) description = 'ближайшего {round_to}'.format(round_to=round_to) else: if random.choice([False, True]): # Write the rounding value as a word instead. if round_to == 10: des = True round_to = display.StringNumber( round_to, case='do', desyatok=des, join_number_words_with_hyphens=False) description = 'ближайших {round_to}'.format(round_to=round_to) elif power == 0 and random.choice([False, True]): # Round to nearest integer. description = 'ближайшего целого числа' else: # Round to decimal places. description = '{dps} знак{ending} после запятой' # if power == 1: # # Plural # description += 's' dps = -power ending = ['а', 'ов', "ов"][check_one_ending(dps)] if random.choice([False, True]): dps = display.StringNumber(dps, case='do') description = description.format(dps=dps, ending=ending) template = random.choice([ 'Округлите {input} до {description}.', 'Сколько получится, если {input} округлить до {description}?', ]) return example.Problem(question=example.question(context, template, input=input_, description=description), answer=value)
def is_integer_polynomial(value): if not is_polynomial(value): return False coefficients = np.reshape(value.coefficients, [-1]) return all(number.is_integer(coeff) for coeff in coefficients)
def sample(self, sample_args, values): """Sample multiple entities. Args: sample_args: Instance of `SampleArgs`. The min and max entropy and module count will be split up betwene the various entities sampled. values: List of values to sample. Returns: List of `Entity` of the same length as `types`. Raises: RuntimeError: If one of the modules generates a non-`Entity`. """ # Can only sample children once. assert self._module_count == 1 assert not self._child_symbols assert not self._child_entities if isinstance(sample_args, PreSampleArgs): sample_args = sample_args() sample_args_split = sample_args.split(len(values)) def all_symbols(): return (self._relation_symbols.union(self._self_symbols).union( self._child_symbols)) for value, child_sample_args in zip(values, sample_args_split): if number.is_integer(value): value = sympy.Integer(value) all_symbols_ = all_symbols() context = Context(all_symbols_) if child_sample_args.num_modules == 0: entity = self._value_entity(value, context) else: sampler = self._sampler(value, child_sample_args) entity = sampler(value, child_sample_args, context) if not isinstance(entity, Entity): raise RuntimeError( 'Expected entity, but got {} instead'.format(entity)) if (not number.is_integer_or_rational_or_decimal(entity.value) and not isinstance(entity.value, Polynomial)): raise RuntimeError( 'sampler {} returned invalid value of type {}'.format( sampler, type(entity.value))) if ((number.is_integer_or_rational_or_decimal(value) and entity.value != value) or (isinstance(value, Polynomial) and not np.array_equal( entity.value.coefficients, value.coefficients))): raise RuntimeError( 'entity values differ, sampler={} wanted={} got={}'. format(sampler, value, entity.value)) if child_sample_args.num_modules != context.module_count: raise RuntimeError( 'unused modules, value={} sample_args={} context.module_count={},' ' sampler={}'.format(value, child_sample_args, context.module_count, sampler)) self._module_count += context.module_count self._child_entities.append(entity) for symbol in context.self_symbols.union(context.child_symbols): assert symbol not in all_symbols_ self._child_symbols.add(symbol) return self._child_entities