Exemple #1
0
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)
Exemple #2
0
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)))
Exemple #3
0
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
Exemple #4
0
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