Exemplo n.º 1
0
def compute_solution_for_recurrence(recurr_coeff: Expr,
                                    inhom_part_solution: Expr,
                                    initial_value: Expr):
    """
    Computes the (unique) solution to the recurrence relation:
    f(0) = initial_value; f(n+1) = recurr_coeff * f(n) + inhom_part_solution
    """
    log(
        f"Start compute solution for recurrence, { recurr_coeff }, { inhom_part_solution }, { initial_value }",
        LOG_VERBOSE)
    n = symbols('n', integer=True, positive=True)
    if recurr_coeff.is_zero:
        return expand(inhom_part_solution.xreplace({n: n - 1}))

    hom_solution = (recurr_coeff**n) * initial_value
    k = symbols('_k', integer=True, positive=True)
    summand = simplify(
        (recurr_coeff**k) * inhom_part_solution.xreplace({n: (n - 1) - k}))
    particular_solution = summation(summand, (k, 0, (n - 1)))
    particular_solution = without_piecewise(particular_solution)
    solution = simplify(hom_solution + particular_solution)
    log(
        f"End compute solution for recurrence, { recurr_coeff }, { inhom_part_solution }, { initial_value }",
        LOG_VERBOSE)
    return solution
Exemplo n.º 2
0
def get_initial_polarity_for_expression(expression: Expr,
                                        program: Program) -> (bool, bool):
    """
    Returns a sound estimate whether a given expression can initial be positive and negative. It does so
    by substituting the variable power in the given expression with all possible combination of lower and upper
    bounds of their respective supports.
    """
    variables = expression.free_symbols.intersection(program.variables)
    # First we can replace n and all variables which are deterministic initially (meaning they have exactly one branch)
    expression = expression.xreplace(
        {symbols("n", integer=True, positive=True): 0})
    for v in variables:
        if not expression.free_symbols & variables:
            continue
        if hasattr(program.initial_values[v], "branches") and len(
                program.initial_values[v].branches) == 1:
            expression = expression.subs(
                {v: program.initial_values[v].branches[0][0]})

    variables = expression.free_symbols.intersection(program.variables)
    if not variables:
        maybePos = bool(
            expression > 0) if (expression > 0).is_Boolean else True
        maybeNeg = bool(
            expression < 0) if (expression < 0).is_Boolean else True
        return maybePos, maybeNeg

    expression = expression.as_poly(variables)
    var_powers = set()
    monoms = get_monoms(expression)
    for m in monoms:
        m = m.as_poly(variables)
        powers = m.monoms()[0]
        var_powers.update([(v, p) for v, p in zip(m.gens, powers) if p > 0])
    initial_supports = get_initial_supports_for_variable_powers(
        var_powers, program)
    possible_substitutions = flatten_substitution_choices(initial_supports)
    expression = expression.as_expr()
    possible_initial_polarities = []
    for ps in possible_substitutions:
        pos = expression.subs(ps) > 0
        if pos.is_Boolean:
            possible_initial_polarities.append(bool(pos))
        else:
            possible_initial_polarities.append(None)

    allPositive = all([v is True for v in possible_initial_polarities])
    allNegative = all([v is False for v in possible_initial_polarities])

    maybePos = not allNegative
    maybeNeg = not allPositive

    return maybePos, maybeNeg