Как получить сортировку ExprCondPair в функции Piecewise, полученной при решении другой функции Piecewise? - PullRequest
0 голосов
/ 19 мая 2018

Давайте рассмотрим следующий пример:

import sympy as sym

x, y = sym.symbols(['x', 'y'])

cdf = sym.Piecewise((0, y < 0), 
                    (y, y < 1), 
                    (2*y - 1, y <= 2), 
                    (3, True))
eq = sym.Eq(x, cdf)
inverse = sym.solve(eq, y, rational=False)  # rational prevents buggy exception
print(inverse)

Вывод:

[Piecewise((x, x < 1), (nan, True)), 
 Piecewise((x/2 + 1/2, x/2 + 1/2 <= 2), (nan, True))]

Это можно легко преобразовать в один кусочек со следующей функцией:

from typing import List

def recreate_piecewise(functions: List[sym.Piecewise]) -> sym.Piecewise:
    """Collects Piecewise from a list of Piecewise functions"""
    return sym.Piecewise(*[piecewise.args[0]
                           for piecewise in functions])


print(recreate_piecewise(inverse))

Вывод:

Piecewise((x, x < 1), (x/2 + 1/2, x/2 + 1/2 <= 2))

Корни условий этого кусочно-го: 1 и 3 .Эти корни упорядочены от самых маленьких до самых больших .

При решении любых других кусочных функций я ожидаю, что части их решений будут отсортированы таким же образом.Но, к сожалению, это не тот случай.


Это можно показать на следующем примере:

cdf = sym.Piecewise((0, y < 4.3), 
                    (y - 4.3, y < 12.9), 
                    (5*y - 55.9, y <= 13.5), 
                    (11.6, True))
eq = sym.Eq(x, cdf)
inverse = sym.solve(eq, y, rational=False)
print(recreate_piecewise(inverse))

Вывод:

Piecewise((x/5 + 11.18, x/5 + 11.18 <= 13.5), 
          (x + 4.3, x + 4.3 < 12.9))

Здесь корни 11,6 и 8,6 , что является другим порядком.


Вопрос:
Как получить кусочные решения, отсортированные всегдав том же порядке?


Что я пробовал:
Я реализовал следующие вспомогательные функции.Это решение работает, но, к сожалению, не во всех случаях.Кроме того, я чувствую, что использовал здесь слишком много обходных путей, и некоторые вещи можно было бы сделать гораздо проще.

from sympy.functions.elementary.piecewise import ExprCondPair


def replace_inequalities(expression: sym.Expr) -> sym.Expr:
    """Replaces <, <=, >, >= by == in expression"""
    conditions = [sym.Lt, sym.Le, sym.Gt, sym.Ge]
    for condition in conditions:
        expression = expression.replace(condition, sym.Eq)
    return expression


def piecewise_part_condition_root(expression_condition: ExprCondPair) -> float:
    """Returns a root of inequality part"""
    condition = expression_condition[1]
    equation = replace_inequalities(condition)
    return sym.solve(equation, x)[0]


def to_be_sorted(piecewise: sym.Function) -> bool:
    """Checks if elements of Piecewise have to be sorted"""
    first_line = piecewise.args[0]
    last_line = piecewise.args[-1]

    first_root = piecewise_part_condition_root(first_line)
    last_root = piecewise_part_condition_root(last_line)

    return last_root < first_root


def sort_piecewise(piecewise: sym.Piecewise) -> sym.Piecewise:
    """Inverts the order of elements in Piecewise"""
    return sym.Piecewise(*[part
                           for part in piecewise.args[::-1]])

И для первого, и для второго примеров это будет работать.
Первый:

cdf = sym.Piecewise((0, y < 0), 
                    (y, y < 1), 
                    (2*y - 1, y <= 2), 
                    (3, True))
eq = sym.Eq(x, cdf)
inverse = sym.solve(eq, y, rational=False)
inverse = recreate_piecewise(inverse)

if to_be_sorted(inverse):
    inverse = sort_piecewise(inverse)
print(inverse)

Вывод:

Piecewise((x, x < 1), 
          (x/2 + 1/2, x/2 + 1/2 <= 2))

Второй:

cdf = sym.Piecewise((0, y < 4.3), 
                    (y - 4.3, y < 12.9), 
                    (5*y - 55.9, y <= 13.5), 
                    (11.6, True))
eq = sym.Eq(x, cdf)
inverse = sym.solve(eq, y, rational=False)
inverse = recreate_piecewise(inverse)

if to_be_sorted(inverse):
    inverse = sort_piecewise(inverse)
print(inverse)    

Вывод:

Piecewise((x + 4.3, x + 4.3 < 12.9), 
          (x/5 + 11.18, x/5 + 11.18 <= 13.5))

Но если я возьму пример, где решение будетвключите функцию LambertW, мой метод потерпит неудачу:

def to_lower_lambertw_branch(*args) -> sym.Function:
    """
    Wraps the first argument from a given list of arguments
    as a lower branch of LambertW function.
    """
    return sym.LambertW(args[0], -1)


def replace_lambertw_branch(expression: sym.Function) -> sym.Function:
    """
    Replaces upper branch of LambertW function with the lower one.
    For details of the bug see:
    https://stackoverflow.com/questions/49817984/sympy-solve-doesnt-give-one-of-the-solutions-with-lambertw
    Solution is based on the 2nd example from:
    http://docs.sympy.org/latest/modules/core.html?highlight=replace#sympy.core.basic.Basic.replace
    """
    return expression.replace(sym.LambertW,
                              to_lower_lambertw_branch)


cdf = sym.Piecewise((0, y <= 0.0), 
                    ((-y - 1)*sym.exp(-y) + 1, y <= 10.0), 
                    (0.999500600772613, True))
eq = sym.Eq(x, cdf)
# Intermediate results are in inline comments
inverse = sym.solve(eq, y, rational=False)  # [Piecewise((-LambertW((x - 1)*exp(-1)) - 1, -LambertW((x - 1)*exp(-1)) - 1 <= 10.0), (nan, True))]
inverse = recreate_piecewise(inverse)  # Piecewise((-LambertW((x - 1)*exp(-1)) - 1, -LambertW((x - 1)*exp(-1)) - 1 <= 10.0))
inverse = replace_lambertw_branch(inverse)  # Piecewise((-LambertW((x - 1)*exp(-1), -1) - 1, -LambertW((x - 1)*exp(-1), -1) - 1 <= 10.0))

if to_be_sorted(inverse):  # -> this throws an error
    inverse = sort_piecewise(inverse)
print(inverse)    

В отмеченной строке выдается ошибка:

---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-8-4ebf5c3828fb> in <module>()
     28 inverse = replace_lambertw_branch(inverse)  # Piecewise((-LambertW((x - 1)*exp(-1), -1) - 1, -LambertW((x - 1)*exp(-1), -1) - 1 <= 10.0))
     29 
---> 30 if to_be_sorted(inverse):  # -> this throws error
     31     inverse = sort_piecewise(inverse)
     32 print(inverse)

<ipython-input-5-d8df4b6ed407> in to_be_sorted(piecewise)
     22     last_line = piecewise.args[-1]
     23 
---> 24     first_root = piecewise_part_condition_root(first_line)
     25     last_root = piecewise_part_condition_root(last_line)
     26 

<ipython-input-5-d8df4b6ed407> in piecewise_part_condition_root(expression_condition)
     14     condition = expression_condition[1]
     15     equation = replace_inequalities(condition)
---> 16     return sym.solve(equation, x)[0]
     17 
     18 

~/.local/lib/python3.6/site-packages/sympy/solvers/solvers.py in solve(f, *symbols, **flags)
   1063     ###########################################################################
   1064     if bare_f:
-> 1065         solution = _solve(f[0], *symbols, **flags)
   1066     else:
   1067         solution = _solve_system(f, symbols, **flags)

~/.local/lib/python3.6/site-packages/sympy/solvers/solvers.py in _solve(f, *symbols, **flags)
   1632 
   1633     if result is False:
-> 1634         raise NotImplementedError('\n'.join([msg, not_impl_msg % f]))
   1635 
   1636     if flags.get('simplify', True):

NotImplementedError: 
No algorithms are implemented to solve equation -LambertW((x - 1)*exp(-1), -1) - 11

1 Ответ

0 голосов
/ 20 мая 2018

Один из подходов состоит в численном определении точек останова (nsolve), которых может быть достаточно для сортировки.Другой, использующий тот факт, что CDF является возрастающей функцией, заключается в сортировке по значениям y вместо значений x;то есть с правой стороны неравенств, которые вы получаете в inverse кусочно.Проиллюстрировано на втором примере:

cdf = sym.Piecewise((0, y < 4.3), (y - 4.3, y < 12.9), (5*y - 55.9, y <= 13.5), (11.6, True))
inverse = sym.solve(sym.Eq(x, cdf), y, rational=False)
solutions = [piecewise.args[0] for piecewise in inverse]
solutions.sort(key=lambda case: case[1].args[1])
print(sym.Piecewise(*solutions))

печатает

Piecewise((x + 4.3, x + 4.3 < 12.9), (x/5 + 11.18, x/5 + 11.18 <= 13.5))

Это должно работать с любой возрастающей функцией, поскольку возрастающий порядок значений y совпадает с возрастающим порядком значений x.

...