In my most recent PR concerning `solve` Oscar has suggested (and I concur) 
that the varieties of output for `solve` not be changed -- though I am not 
sure that we are doing so for the same. Here is a draft for the 
documentation's "Explanations" section:

<p>
The output of the `solve` function can seem very unwieldy since it may 
appear to arbitrarily return one of 6 different types of output (in 
addition to raising errors). The reasons for this are historical and are 
biased toward human interaction rather than programatic use. The type of 
output will depend on the type of equation(s) (and how they are entered) 
and the number of symbols that are provided (and how they are provided).

Since this output is controllable by using the `dict=True` flag to give 
either a boolean expression or a list of dictionaries, the following is an 
explanation for those that might prefer the default output but wish to 
understand better why it is being given to make better sense of the output 
of an interactive session.

1. an empty list indicates that no solution was found

    >>> solve(sqrt(x) + 1)
    []

2. a list of values corresponding the solutions for a single variable which 
should be obvious in context because a) the equation was univariate or b) a 
single symbols was specified as being of interest.

    >>> solve(x**2 - 4)
    [-2, 2]
    >>> solve(x - y - 1, x)
    [y + 1]

3. a single dictionary with keys being symbols and values being the 
solutions for those symbols - this is the result when one or more equations 
passed as a list has only a single, unique solution (e.g. the system is 
linear or else is monotonic).

    >>> solve([x + y - 2, x - y + 2], x, y)
    {x: 0, y: 2}

4. a list of tuples, each giving a set of values for the symbols in the 
order they were given - this is the result when more than one symbol was 
given in a well defined order with 1) a single expression or 2) a list of 
one more more expressions was given and one or more of them was not 
linear/monotonic.

    >>> solve(x - 1, x, y)
    [(1, y)]
    >>> solve([x**2], x)
    [(0,)]
    >>> solve([x**2 - 1], x)
    [(-1,), (1,)]
    >>> solve([x**2 - 1], x, y)
    [(-1, y), (1, y)]

5. a list of dictionaries, each containing a solution for the variables 
appearing as keys in the dictionary - this is used when the expression (or 
an expression in the list) passed for ``f`` is neither linear nor monotonic 
and the symbols were either not provided or else provided in an unordered 
container like a list.

    >>> solve([exp(x) - 1, x**2])
    [{x: 0}]
    >>> solve([x + y - 2, x**2 - y + 2])
    [{x: -1, y: 3}, {x: 0, y: 2}]
    >>> solve([x + y - 2, x**2 - y + 2])
    [{x: -1, y: 3}, {x: 0, y: 2}]

6. a boolean expression - this will happen when an relational expression 
other than an Equality is given as an expression to solve. A single 
equality, or more complicated relational expression might be returned. 
Simple constraints to limit output to integers or positive values can be 
accomplished by setting the assumptions on the variables for which a 
solution is being sought, e.g. ``x = Symbol('x', positive=True)``, before 
defining the expression and passing it to `solve`.

    >>> solve([x**2 - 4, Ne(x,-2)])
    Eq(x, 2)

For those that used a version of SymPy older than [TBD], a complicating 
factor was that `solve` automatically tried to detect when an equation 
might be solved by matching coefficients on powers of an unspecified 
symbol. That would happen when a single expression was passed with all but 
one of the free symbols given as symbols for which a solution was desired. 
The output would be a single dictionary (if the symbols appeared in a 
linear fashion) or else a list of tuples if the system could be solved, 
otherwise an algebraic solution was sought for one or more of the symbols.

    >>> solve(a*x + b - (3*x + 4), a, b)
    {a: 3, b: 4}
    >>> solve(a*x + b**2 - (3*x + 4), a, b)
    [(3, -2), (3, 2)]

Now, only the algebraic solution for one or more of the specified symbols 
is sought. Determination of values for undetermined coefficients is 
obtained by explicitly passing the constraints on the coefficients or by 
calling `solve_undetermined_coeffs` directly.

    >>> solve_undetermined_coeffs(a*x + b - (3*x + 4), [a, b], x)
    {a: 3, b: 4}
    >>> solve([a - 3, b**2 - 4])
    [(3, -2), (3, 2)]
</p>

The problem with a global `solve` flag like is used for `evaluate` to force 
use the `dict=True` is that this would break internal code that depended on 
the current ouput. Instead, locally, at the beginning of an interactive 
session, the following could be defined:

    from sympy import solve as __solve
    def solve(a, *b, **c):
        from sympy.utilities.iterables import iterable
        if b:
            if len(b) == 1 and iterable(b[0]):
                return __solve(a, b[0], **c, dict=True)
            return __solve(a, *b, **c, dict=True)
        return __solve(a, **c, dict=True)

The first step in removing the automatic identification of undetetermined 
coefficient systems in implemented in PR #23699: a flag can disable this 
detection so an algebraic result is returned.

/c
On Monday, July 4, 2022 at 10:41:11 PM UTC-5 Chris Smith wrote:

> Here is the current logic of the input and output for `solve`
>
> if equation is a boolean expression or a list contains a boolean
>     boolean value or expression
>         >>> solve((x**2-4, x>1))
>         Eq(x, 2)
>         >>> solve((x**2-4, x>2))
>         False
>
> else expression or list of expression(s) is passed
>     and there is no solution
>         return []
>     else
>         equation is single expression
>             0 symbols
>                 univariate - list of values
>                 else - list of dictionaries
>             1 symbol
>                 list of values
>             > 1 symbol
>                 ordered symbols (e.g. list) -> list of tuples
>                 else -> list of dictionaries
>         list of 1 or more expressions for equation with
>             0 symbols
>                 linear in all variables - dict
>                 else - list of dictionaries
>             1 symbol
>                 single solution - dict
>                 else - list of tuples
>             > 1 symbol
>                 linear system and #eqs = #symbols -> dict
>                 ordered symbols (e.g., list) -> list of tuples
>                 else -> list of dictionaries
>
> Except for booleans, using `dict=True` should return a list of 
> dictionaries.
>
> There is some inconsistency when using functions instead of symbols that I 
> will try to fix in https://github.com/sympy/sympy/pull/23699.
>
> /c
> On Friday, March 25, 2022 at 10:37:41 AM UTC-5 Chris Smith wrote:
>
>> Oscar gave an example in https://github.com/sympy/sympy/issues/16590
>>
>> solve(x**2+y**2-1, [x, y]) -> [(cos(C₁), sin(C₁))]
>>
>> This is a good example of where the purpose of solve should be clarified. 
>> I have always approached it as trying to solve for some (or all) of the 
>> given symbols explicitly in terms of others. I am not sure what I would 
>> call the above example - parametrization?
>>
>> /c
>> On Thursday, March 24, 2022 at 2:26:36 PM UTC-5 Chris Smith wrote:
>>
>>> Is there an example of when a parameter other than an integer would be 
>>> necessary?
>>>
>>> /c
>>>
>>> On Thursday, March 24, 2022 at 2:21:07 PM UTC-5 Aaron Meurer wrote:
>>>
>>>> On Wed, Mar 23, 2022 at 5:52 PM Chris Smith <[email protected]> wrote: 
>>>> > 
>>>> > Except for booleans, consider single and system output: 
>>>> > 
>>>> > If it is a single equation the output depends on whether symbols are 
>>>> given or not: 
>>>> > 
>>>> > * list of expressions if a single variable is given (or the 
>>>> expression is univariate and no symbol is given) 
>>>> > * if multiple variables are given and the system is solved by linear 
>>>> undetermined coefficients or is split into real and imaginary parts you 
>>>> get 
>>>> a dictionary, otherwise you get a list of tuples of values 
>>>> > * a list of dictionaries if no variables are given (since otherwise 
>>>> you wouldn't know for which symbol the solution corresponds) 
>>>> > 
>>>> > If it is a system of equations (defined as 1 or more equations in an 
>>>> iterable) then the solution is: 
>>>> > 
>>>> > * a single dictionary if the simplified system was solved as a linear 
>>>> system 
>>>> > * if multiple variables are given and the system is solved as a 
>>>> linear system you get a dictionary (and extraneous symbols are ignored), 
>>>> otherwise a list of tuples of values 
>>>> > * a list of dictionaries if no variables were specified (or else you 
>>>> wouldn't know for which symbol the solution corresponds) 
>>>> > 
>>>> > Although the docstring does not lay out the return cases as I have 
>>>> just done, it does try to show the situation under which the different 
>>>> types are returned. (I wrote them and I was mainly the messenger.) 
>>>> > 
>>>> > Any user that is concerned about the return value has had the option 
>>>> to select `set` or `dict`ionary output for several releases now. There 
>>>> should be no reason that it can't be used reliably in a programmatical 
>>>> way. 
>>>> > 
>>>> > As has been pointed out, some of the issues arise from `solve` trying 
>>>> to guess what the user might have wanted. This is against the admonition 
>>>> to 
>>>> "refuse the temptation to guess". Others have pointed out the mixing of 
>>>> idioms of "eliminating" vs "solving". 
>>>>
>>>> Another problem is how to return solutions that have a parameter, 
>>>> where the parameter is a new variable. For example, the exp(I*theta) 
>>>> solution of the abs(z) = 1 example I gave above. This also occurs with 
>>>> most diophantine equations. At best it could just return the 
>>>> parameterized solution, and hope the user knows how to extract it. But 
>>>> even that only really works if the range of the parameter is something 
>>>> you can represent using the old assumptions (e.g., you could add 
>>>> Dummy('theta', real=True) but "theta in [0, 2pi)" wouldn't be 
>>>> representable). Ideally the parameters would be returned separately 
>>>> somehow, including their range of values as a set. 
>>>>
>>>> This was actually one of the original motivations for solveset 
>>>> (consider solve(sin(x))), but ironically, even though such things are 
>>>> representable from solveset, extracting the parameter from a set ends 
>>>> up being harder than it would be if solve() just returned Dummy('n', 
>>>> integer=True)*pi. 
>>>>
>>>> Aaron Meurer 
>>>>
>>>> > 
>>>> > /c 
>>>> > 
>>>> > On Wednesday, March 23, 2022 at 5:39:12 PM UTC-5 Oscar wrote: 
>>>> >> 
>>>> >> On Wed, 23 Mar 2022 at 19:42, Aaron Meurer <[email protected]> 
>>>> wrote: 
>>>> >> > 
>>>> >> > So I'm starting to wonder if the real fix here isn't so much to 
>>>> "fix 
>>>> >> > solve" (although solve() should definitely be improved and cleaned 
>>>> >> > up), but rather to treat solve() as the "interactive only" 
>>>> function 
>>>> >> > for equation solving, just as simplify() is the "interactive only" 
>>>> >> > function for simplification. 
>>>> >> 
>>>> >> I don't think we can save solve this way. Even if I wanted to make 
>>>> an 
>>>> >> "interactive" solve function I wouldn't want it to be so 
>>>> inconsistent. 
>>>> >> Most of the return types are awkward even for interactive use. 
>>>> >> Remember the number one thing someone wants to do with the output: 
>>>> >> substitute it into something. 
>>>> >> 
>>>> >> There are so many other problems with solve internally and 
>>>> externally 
>>>> >> that whichever way you look at it the whole thing needs to be 
>>>> >> rewritten from scratch. It's impossible to do that while preserving 
>>>> >> the existing behaviour which is itself impossible to even define 
>>>> (the 
>>>> >> docstring doesn't even try!). 
>>>> >> 
>>>> >> The only actionable takeaway I see from the idea that solve could be 
>>>> >> considered "interactive only" is that maybe backwards compatibility 
>>>> >> could be ignored and it could be okay to change the output to be a 
>>>> >> list of dicts always. 
>>>> >> 
>>>> >> I agree with the idea that solve should be implemented as a wrapper 
>>>> >> around more carefully defined routines but ultimately there still 
>>>> >> always needs to be a "find explicit solutions to these arbitrary 
>>>> >> equations if possible" routine and it should be possible to use that 
>>>> >> in a programmatic way. 
>>>> >> 
>>>> >> -- 
>>>> >> Oscar 
>>>> > 
>>>> > -- 
>>>> > You received this message because you are subscribed to the Google 
>>>> Groups "sympy" group. 
>>>> > To unsubscribe from this group and stop receiving emails from it, 
>>>> send an email to [email protected]. 
>>>> > To view this discussion on the web visit 
>>>> https://groups.google.com/d/msgid/sympy/8e0c6289-8e46-41e7-b755-5216eb9a2dc9n%40googlegroups.com.
>>>>  
>>>>
>>>>
>>>

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sympy/69f58eb7-a7da-4c35-8359-668f4b9b254an%40googlegroups.com.

Reply via email to