We need some community input on the desired behavior of an equation class...

236 views
Skip to first unread message

gu...@uwosh.edu

unread,
Jan 6, 2021, 10:03:57 PM1/6/21
to sympy
I have developed a SymPy tool that allows manipulation of equations (two expression equal to each other, e.g. `a*b = c/d`) using as close to standard mathematical notation as I could manage. This is primarily meant for interactive use when doing manual algebra, but has some potential uses as a backend for automatic symbolic manipulations.

While working to make it possible to incorporate this tool into the sympy codebase some questions have arisen about how this would be used and thus the best way to interface it with sympy. Therefore we need a broader community discussion of this tool.

Anybody can try this tool interactively by following this mybinder.org link: https://mybinder.org/v2/gh/gutow/Algebra_with_Sympy.git/master. The demonstrations of the capabilities are in the Jupyter notebook `Demonstration of equation class.ipynb`.

We are interested in knowing if people find any of the behavior odd and why. In particular we are not sure about the best behavior for differentiation and integration, so please look at those sections carefully. We could also use input on operations that you would like to work that do not and how they should behave.

Please reply to this discussion thread.

Thank you,
Jonathan

S.Y. Lee

unread,
Jan 9, 2021, 6:57:09 AM1/9/21
to sympy
I have previously questioned that Wolfram have a == b -> a/c == b/c transformation in conditional expression format
https://reference.wolfram.com/language/ref/DivideSides.html and why you were not attempting to implement same like this,
But now I'd may agree on that if we treat such examples as polynomial algebra, those transformation can make mathematical sense without connection to the assumptions behind.
And I believe that in this sense, differentiation and integration can similarly avoid such hoax without treatment of assumptions.

But now I have an another question if the manipulation works in algebraically robust way,
For example, we have lots of pitfalls of expression tree manipulation that some simple examples like "x / x" work,
but we soon end up with some more complicated examples like " exp(x+1)/exp(1)" doesn't work robustly,
and the only way to cope with them are appending .expand().cancel().together().simplify(), ... to the tail which is pretty much black-boxed and not robust at all.

So this is one of the problem that I'm skeptical about how they can be used as a backend for automatic symbolic manipulations, rather than some convenience tools for end users.
And for such purposes I believe that we rather have to make object like PolyEquation.

David Bailey

unread,
Jan 9, 2021, 10:24:02 AM1/9/21
to sy...@googlegroups.com
On 09/01/2021 11:57, S.Y. Lee wrote:
I have previously questioned that Wolfram have a == b -> a/c == b/c transformation in conditional expression format
https://reference.wolfram.com/language/ref/DivideSides.html and why you were not attempting to implement same like this,
But now I'd may agree on that if we treat such examples as polynomial algebra, those transformation can make mathematical sense without connection to the assumptions behind.
And I believe that in this sense, differentiation and integration can similarly avoid such hoax without treatment of assumptions.

But now I have an another question if the manipulation works in algebraically robust way,
For example, we have lots of pitfalls of expression tree manipulation that some simple examples like "x / x" work,
but we soon end up with some more complicated examples like " exp(x+1)/exp(1)" doesn't work robustly,
and the only way to cope with them are appending .expand().cancel().together().simplify(), ... to the tail which is pretty much black-boxed and not robust at all.

So this is one of the problem that I'm skeptical about how they can be used as a backend for automatic symbolic manipulations, rather than some convenience tools for end users.
And for such purposes I believe that we rather have to make object like PolyEquation.

I suppose I think the problem is that many users DO use SymPy as a tool to do calculations - not as something to build into something else. As you acknowledge, such convenience functions are almost essential to make good use of an equation class interactively, even though they can produce mistakes when wrongly used (as can subs).

One possibility might be to place 'dangerous' functions, such as DivideSides, in a submodule of SymPy so that they are only loaded if the user, or the system builder, deems it appropriate - very much as users can choose to import the symbols from sympy.abc at their discretion.

Obviously such functions would not be used by default by simplify if  it were applied to equation objects.

David


gu...@uwosh.edu

unread,
Jan 10, 2021, 8:27:30 AM1/10/21
to sympy
On Saturday, January 9, 2021 at 5:57:09 AM UTC-6 syle...@gmail.com wrote:
But now I have an another question if the manipulation works in algebraically robust way,
For example, we have lots of pitfalls of expression tree manipulation that some simple examples like "x / x" work,
but we soon end up with some more complicated examples like " exp(x+1)/exp(1)" doesn't work robustly,
and the only way to cope with them are appending .expand().cancel().together().simplify(), ... to the tail which is pretty much black-boxed and not robust at all.

At present the `Equation` class does no extra simplification that `sympy` does not already do. Are you asking for the `Equation` class to automatically apply extra simplification steps? Here is the present behavior of a standard `sympy Expr` and the proposed `Equation` classes for your simple example.

>>> expr1 = exp(x+1)
>>> expr2 = expr1/exp(x)
>>> expr2 exp(-x)*exp(x + 1)
>>> eq1 = Eqn(a, expr1)
>>> eq2 = eq1/exp(x)
>>> eq2
a*exp(-x) = exp(-x)*exp(x + 1)
>>> expand(eq2)
a*exp(-x) = E
>>> expand(expr2) E

Thanks for the input.
Jonathan

gu...@uwosh.edu

unread,
Jan 10, 2021, 8:34:39 AM1/10/21
to sympy
David,

Can you explain further what you are thinking in terms of dangerous functions? At present `Equations` supports doing things to only one side through syntax like `.applylhs(method, *args)` and less generally `.dorhs.method()`. So I think that `Equation` may already support what you are suggesting. Is that correct? Otherwise except in the cases of differentiation and integration operations are always applied symmetrically to both sides.

Thanks for the comments.
Jonathan

Garth Snyder

unread,
Jan 10, 2021, 5:14:10 PM1/10/21
to sy...@googlegroups.com
> gu...@uwosh.edu wrote: I have developed a SymPy tool that allows manipulation of equations...

Thanks for working on this.

I use Sympy both for math inside of code and as an interactive tool. The current Eq() seems like a bit of an afterthought, so historically I haven’t made much use of it. However, I’m actually a bit surprised to learn that the existing Eqs don’t behave exactly as demonstrated in the Eqn notebook, at least as far as the basic idea that “equations are expressions; anything you do to the equation is propagated to both sides”. Or in other words, Eqns seem to be doing pretty much exactly what I would expect.

Current Eqs just generate an error when you try to do equation-level manipulations in the manner of Eqn. Are there specific points of conflict that prevent the new facilities from being integrated into the existing Eq system?

Garth

gu...@uwosh.edu

unread,
Jan 10, 2021, 5:45:26 PM1/10/21
to sympy
Garth,

Presently it is not practical to combine them, although we have done some experiments aimed that direction. `Eq` actually represents the logical concept of an equality and evaluates to `True`, `False` or just returns `Eq(lhs, rhs)` if the validity cannot be determined. I, personally,  believe that once the general `Relational` class (<, >, !=, etc..) is functioning in a way that it can take over the work of the older assumptions module, we will be able to better combine the behaviors, but not at present. Even when the work reaches this stage there are some issue that will need to be resolved concerning interactive versus module behavior. To see much of the most recent discussion and work  look at pull request #20723.

For the near term `Equality` (`Eq`) and `Equation` (`Eqn`) will have to stay separate entities, but people working on this have been thinking about the combination.

Is there any behavior that you think it is important to add or change in the proposed `Eqn` class?

Thanks for the feedback.

Jonathan

gu...@uwosh.edu

unread,
Jan 13, 2021, 9:57:38 PM1/13/21
to sympy
Because there have been some requests off-list, I have converted the demonstration code into a PyPi module. You can install with `pip install -U Algebra-with-SymPy` or `pip install -U Algebra_with_SymPy`. The import statement is now `from algebra_with_sympy import *`.

The BinderHub demonstration has also been updated to reflect this.

I added an undocumented experimental feature with this update. You can now use `solve(Eqn, var_to_solve_for)` with the Eqn having any valid SymPy expressions on the lhs and rhs. If you put in an `Eqn` the solutions are returned as a list of `Equations`. The functionality is far from complete, but it provides an idea of how this might behave in the future. Comments and suggestions appreciated.

Jonathan

S.Y. Lee

unread,
Jan 16, 2021, 2:41:10 AM1/16/21
to sympy
I think that the clear difference between Eq and Eqn should be.
So if the operations like "divide sides" are going to be implemented for `Eq` in parallel, I think that it's better to see more logical framework there (like giving conditions where division is possible or not), to distinguish it from Eqn.

On Thursday, January 7, 2021 at 12:03:57 PM UTC+9 gu...@uwosh.edu wrote:

Aaron Meurer

unread,
Jan 19, 2021, 6:34:47 PM1/19/21
to sympy
Is there a pull request with the code associated with this, or is it
only in the Binder for now?

The end-user API looks good to me. I think there may be some technical
questions on the specifics on the implementation, but those wouldn't
affect how things look at the end. The only thing that stands out to
me as a possible wart is that there are two identical attributes for
swapping an equation, .reversed and .swap. I would just pick one. My
preference would be for the shorter one just because it's easier to
type.

I would also remove the "check" option. There needs to be a clear
distinction between Eq(), which is a boolean (i.e., it is either True
or False, though it's exact value may be symbolic), and Eqn() which
represents a manipulatable left-hand side and right-hand side. Eqn()
shouldn't produce an error if the sides are unequal. The whole point
of Eqn should be to be able to take something like Eqn(x, x + 1) and
manipulate it in a way that shows that it is inconsistent. For
example, suppose you have a set of equation objects representing a
system of linear equations. You could directly manipulate them using
the elementary row operations to solve the system
(https://en.wikipedia.org/wiki/Gaussian_elimination#Row_operations).
The system is inconsistent if and only if the equation 1 = 0 can be
produced. If this resulted in an error, then it would interrupt the
flow of solving the system. The whole point of Eqn() should be to
represent equations and manipulate them in a way that one can
determine if they do or don't have solutions. At best, there could be
a method on Eqn() that tries to determine if the equation has zero
solutions (e.g., as_Boolean() or solve()).

Another potential gotcha point is that you currently allow performing
any function to both sides of an equation. This includes functions
that aren't one-to-one, meaning doing so can introduce solutions. I
don't think this is something that should be disallowed, but it should
perhaps be mentioned in the documentation. This is also a "gotcha" in
standard algebra, it's just worth noting that we don't do anything to
try to guard against it. Rather, Eqn() just uses the simple design
where Eqn + Eqn just adds corresponding sides, and Eqn() + expr adds
expr to both sides (and the same if you replace + with any function or
operation).

Something that is missing from this API is substitution. We already
decided that subs() shouldn't work with the boolean Eq(), but I think
it makes sense for Eqn() (see
https://github.com/sympy/sympy/issues/14741). This is actually another
reason to not consider something like Eqn(x, x + 1) as invalid. As a
boolean equality, it is meaningless, but as input to subs() it makes
sense. A more interesting case is substituting one equation in
another. This is important if we think of Eqn() as an object that can
be used to manipulate equalities in a way that would typically be done
when solving a system of equations. If you have x + y = 4 and x*y = 4,
the typical way to solve the system by hand would be to isolate one of
the variables in one of the equations, and substitute it in the other.
Something like

eq1 = Eqn(x + y, 4)
eq2 = Eqn(x*y, 4)
eq1 = eq1 - y # subtract y from both sides, isolating x
eq2 = eq2.subs(eq1) # This produces Eqn((4 - y)*y, 4)
solve(eq2)

Isolation is also maybe something that could be easier too. Should
solve(Eqn()) always return another Eqn (or list of Eqn)? One could
also consider how one would solve eq2 by hand. Should there be a
method to split products into cases, like Eqn(A*B, 0) -> [Eqn(A, 0),
Eqn(B, 0)]?

Aaron Meurer
> --
> 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 sympy+un...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/2fc7dc45-dd5f-4186-a1a3-f49edcb7c64dn%40googlegroups.com.

gu...@uwosh.edu

unread,
Jan 19, 2021, 7:58:34 PM1/19/21
to sympy
Aaron,
Thank you for the comments. I need go through them more carefully, but I did want to direct you to the SymPy pull request for the `Eqn` class that implements everything in the Binder except for the ability to use `solve` on an `Eqn`. My preference would be to get the `Eqn` class into SymPy and then make it work with solve as a separate project/pull.

As you will see there has been quite a bit of discussion within the pull request. Hence this request for more input from the community.

Jonathan

David Bailey

unread,
Jan 20, 2021, 7:59:40 AM1/20/21
to sy...@googlegroups.com

Hi Group,

I know this isn't strictly a SymPy question, but I suspect someone here will be able to point me in the right direction.

When I first acquired SymPy on my 64-bit Windows 10 machine, I downloaded what was then the official version of Python - 3.7.3, and installed SymPy and some other things upon it. Recently I wanted to install Tkinter, and I got the following diagnostic:

pip install tkinter

Collecting tkinter
  ERROR: Could not find a version that satisfies the requirement tkinter (from versions: none)
ERROR: No matching distribution found for tkinter

I really don't want to start from scratch installing Python, does anyone know what this problem is all about?

I've seen suggestions that tkinter is built into Python, but it seems not:

import tkinter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'tkinter'

David

Naman Nimmo

unread,
Jan 20, 2021, 8:36:30 AM1/20/21
to sy...@googlegroups.com
 
You can install the tkinter package using:  sudo apt-get install python3-tk 
(safe to run on debian-derived distributions)

And import it this way:

>>> import tkinter
>>> tkinter.TkVersion
8.6





--
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 sympy+un...@googlegroups.com.

Oscar Benjamin

unread,
Jan 20, 2021, 8:54:39 AM1/20/21
to sympy
I think David is using Windows so that apt-get command which is for
Debian Linux can not be used.

David I thought that the standard Python installers for Windows would
mean that you had tkinter installed. I don't think you can install it
with pip because it's basically part of Python (so not really a
"separate" package). There are many different ways of installing
Python though so it might depend on exactly which installer you used.

I would generally use the installer files from here:
https://www.python.org/downloads/

Oscar
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/CALkUZDmDgh1c%2BYQh_G5rGY02dUNaFM5%2BUDEu0X0CX_L_mv-f1Q%40mail.gmail.com.

Oscar Benjamin

unread,
Jan 20, 2021, 3:45:44 PM1/20/21
to sympy
I'll add my thoughts in a lengthy post here.

Firstly some background on why the existing Eq (full name Equality)
class is problematic. The Eq class is a Boolean and its intention is
to represent the truth of an expression. What this means is that it
will often evaluate to True or False e.g.:

>>> x = Symbol('x')
>>> Eq(x, 1)
Eq(x, 1)
>>> Eq(1, 2)
False
>>> Eq(1, 1)
True

This is useful in contexts where we want to use the Boolean-ness
because it allows simplifications to happen. Here simply substituting
a value for x into Eq(x, 1) turns it into a True or False and then the
Piecewise can simplify automatically.

>>> p = Piecewise((1, Eq(x, 1)), (2, True))
>>> p
Piecewise((1, Eq(x, 1)), (2, True))
>>> p.subs(x, 1)
1
>>> p.subs(x, 2)
2

However this can be quite awkward in other contexts when you want to
manipulate equations. Any code that operates with instances of Eq
needs to be prepared for the possibility that any operation (subs,
simplify, evalf etc) might happen to turn the Eq into True or False
which then does not have the same methods or attributes as Eq. For
example:

>>> eq = Eq(x, 2)
>>> eq
Eq(x, 2)
>>> eq.lhs
x
>>> eq.subs(x, 1)
False
>>> eq.subs(x, 1).lhs
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'BooleanFalse' object has no attribute 'lhs'

That means that code that operates on Eq needs a lot of checking to be
robust (and many bugs in sympy come from forgetting this). It's also
necessary to check for these every time you have a function like solve
or linsolve etc that can take a list of equations as inputs. The
possible presence of True/False needs to be checked in all inputs
every time before we can begin to e.g. ask for the rhs of the
equation.

For interactive use it would be nice to be able to do things like 2*eq
to multiply both sides of an equation. We could make that work with Eq
but we would have the danger that at any time any operation might turn
into True/False and then subsequent arithmetic operations would fail
because 2*True is meaningless.

It's not just arithmetic but any operation that you might want to
apply to one or both sides of an equation. For example to factor both
sides of an equation you have to do

eq = Eq(factor(eq.lhs), factor(eq.rhs))

which doesn't look so bad but then for example integrating both sides
wrt x from 0 to 1 looks like:

eq = Eq(Integral(eq.lhs, (x, 0, 1)), Integral(eq.rhs, (x, 0, 1))

which begins to show the awkwardness of needing to repeat the same
operation. There are ugly constructs to work around the need to avoid
repetition such as

eq = Eq(*(Integral(side, (x, 0, 1)) for side in [eq.lhs, eq.rhs]))

but that's fairly awkward and cryptic in itself.

The proposal here adds a new Eqn (full name Equation) class that does
not evaluate to True/False. It also defined various operations like
2*eq to make interactive manipulation of equations easier. Here's a
demonstration using that to derive a formula for cos in terms of exp:

>>> theta = Symbol('theta', real=True)
>>> eq = Eqn(exp(I*theta), cos(theta) + I*sin(theta))
>>> eq
exp(I*theta) = I*sin(theta) + cos(theta)
>>> conjugate(eq)
exp(-I*theta) = -I*sin(theta) + cos(theta)
>>> (eq + conjugate(eq))/2
exp(I*theta)/2 + exp(-I*theta)/2 = cos(theta)
>>> _.reversed
cos(theta) = exp(I*theta)/2 + exp(-I*theta)/2

This kind of derivation won't work in general with Eq because at any
step it could evaluate to True. All of the equations above are true -
that's the whole point in a derivation!

So I really like this feature and I want to get some form of it into
the next release. There is a longstanding issue to add this to sympy:
https://github.com/sympy/sympy/pull/19479
Some time ago I had a quick go at adding it myself but it turned out
other things needed to be fixed first. In the end I fixed those other
things but didn't get round to adding the new Equation class itself.

It is worth trying to get the details right though. In this proposal
there are several mechanisms for applying an operation to either the
lhs, rhs, or both sides of an equation:

1. Methods apply/applylhs/appylrhs to apply a function to either/both sides
2. Methods do/dolhs/dorhs to call a method on either/both sides
3. Some functions work on the equation like together(eq)
4. Some methods are defined on the equation like eq.together()

Here's a demo of these:

>>> x = Symbol('x')
>>> eq = Eqn(x*(x + 1), x**2 + x)
>>> eq
x*(x + 1) = x**2 + x
>>> expand(eq)
x**2 + x = x**2 + x
>>> eq.applylhs(expand)
x**2 + x = x**2 + x
>>> factor(eq)
x*(x + 1) = x*(x + 1)
>>> eq.applyrhs(factor)
x*(x + 1) = x*(x + 1)
>>> eq.dorhs.factor()
x*(x + 1) = x*(x + 1)
>>> eq.factor()
x*(x + 1) = x*(x + 1)

The first question that comes to mind is do we need all of these?

If eq.dorhs was callable then it could replace eq.applyrhs and we
could use eq.dorhs(factor) instead of eq.applyrhs(factor).

For many operations that only apply to one side only it is not that
bad to do eq = Eqn(eq.lhs, factor(eq.rhs)). We could even have a
setrhs method so it becomes eq = eq.setrhs(factor(eq.rhs)) although
doesn't seem like a major advantage.

For common operations like factor it maybe makes sense to add those as
methods on Eqn but otherwise are we going to want to add basically all
of the methods that Expr has?

Functions can be made to work with Eqn like factor(eq) but where do we
draw the line with this? As soon as we have Eqn and there are some
functions that can work with it then there will be an expectation to
be able to pass Eqn to almost any possible function and we will have
to add support for it everywhere.

The PR adds a special case in Function.__new__ to make the following work:

>>> eq
x*(x + 1) = x**2 + x
>>> cos(eq)
cos(x*(x + 1)) = cos(x**2 + x)

Adding that special case in Function.__new__ makes it work for most
common mathematical functions but then the question is how to handle
functions that take more than one argument:

>>> atan2(eq, 1)
atan2(x*(x + 1), 1) = atan2(x**2 + x, 1)
>>> atan2(1, eq)
atan2(1, x*(x + 1) = x**2 + x)
>>> atan2(eq, eq)
atan2(x*(x + 1), x*(x + 1) = x**2 + x) = atan2(x**2 + x, x*(x + 1) = x**2 + x)

The last example has generated an equation with nonsensical objects on
each side. Of course that aspect of the PR can be improved but I show
that example to illustrate the more general point that if we have an
expectation that we can pass an Eqn in place of an expression to any
function then we need a way to draw the line between what should work
and what should not. Also adding this to Function.__new__ covers a lot
of Expr subclasses but there are still plenty more that have their own
__new__ methods and the expectation will arise that all of them should
be able to handle Eqn.

This is my biggest concern: making things like cos(eq) work in a way
that seems coherent for users requires adding little bits of support
code widely across the codebase. However we do that there will always
be gaps and in general I think it will give the impression that sympy
is buggy (other things like this already have that effect).

I would much rather stick to an API that can work in general without
risk of bugs and I would prefer users to learn something that will
always work. That means that rather than having cos(eq) I think it
would be better to use eq.apply(cos) or perhaps there could be an
apply function like apply(eq, cos). More complicated cases can be
handled with a lambda function like apply(eq, lambda x: (cos(x) +
1)/2)

With a mechanism like apply it's clear that the function we apply to
the sides of the equation needs to be a callable that can only take
one argument so there is no confusion with something like atan2. This
approach also generalises completely to any function that you could
apply to the sides including both symbolic functions like cos and
manipulation routines like trigsimp. That way users only have to learn
one thing that can always work and is always well defined. It also
means that there is no need to add haphazard support for Eqn
throughout the codebase.

There are a couple of other quirks in the PR such as:

>>> Derivative(eq)
Derivative(x*(x + 1) = x**2 + x, x)
>>> Derivative(eq).doit()
2*x + 1 = 2*x + 1

The unevaluated Derivative in the first output there is nonsensical.
We also have e.g.

>>> P, V, T = symbols('P, V, T')
>>> eq = Eqn(P*V, T)
>>> eq
P*V = T
>>> diff(eq, T)
Derivative(P*V, T) = 1
>>> diff(eq, T).doit()
0 = 1

Here the way the derivative is evaluated treats the lhs and rhs
differently giving an unevaluated derivative on the left. I think the
idea is to prevent the left hand side from fully evaluating although
it will if you call doit. I expect that a lot of users will find this
surprising (I certainly wouldn't want/expect this effect from
differentiating the equation).

Another quirk is the way that integration is handled:

>>> integrate(eq, T)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/enojb/current/sympy/sympy/sympy/integrals/integrals.py",
line 1567, in integrate
integral = Integral(*args, **kwargs)
File "/Users/enojb/current/sympy/sympy/sympy/integrals/integrals.py",
line 81, in __new__
return function._eval_Integral(*symbols, **assumptions)
File "/Users/enojb/current/sympy/sympy/sympy/core/equation.py", line
491, in _eval_Integral
raise ValueError('You must specify `side="lhs"` or `side="rhs"` '
ValueError: You must specify `side="lhs"` or `side="rhs"` when
integrating an Equation

You have pass a "side" argument to integrate to specify which side to integrate:

>>> integrate(eq, T, side='lhs')
P*T*V

I would rather just pass the lhs to integrate if that's all this is doing:

>>> integrate(eq.lhs, T)
P*T*V

I think that for integration and differentiation we should stick to
the general approach implied by apply or do e.g. we use apply to
integrate both sides and applylhs to integrate one side.

>>> eq.apply(lambda s: Integral(s, (x, 0, 1)))
Integral(P*V, (x, 0, 1)) = Integral(T, (x, 0, 1))

If we want a convenience method for integrating both sides like
eq.integrate(x, (x, 0, 1)) then that could make sense. I could
potentially countenance diff and integrate as functions that make a
specific exception to support Eqn so that diff(eq, x) works. I do not
think that Derivative(eq) should be allowed though as the unevaluated
derivative of an equation is nonsensical as a symbolic expression.

I think this is what we want and we should try to get it in for the
next release. I have problems with specific parts of it though. As
discussed on GitHub Jonathan and I are in disagreement over things
like whether cos(eq) should work. We would like to hear what others
think about these kinds of details about how working with Eqn should
work or what makes more sense as an interface. That's why we have
brought the discussion here to the mailing list so if anyone has any
thoughts about any of these things then please say so.

--
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 sympy+un...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/3627a868-7cf0-42a8-92b2-b29b723ddca5n%40googlegroups.com.

gu...@uwosh.edu

unread,
Jan 20, 2021, 5:49:21 PM1/20/21
to sympy
First, Oscar thank you for taking the time to summarize for everyone the key issues we are trying to make decisions on.

Second, I think I can help people understand a little more about why these are issues.

Most of them arise because in code we make a distinction between a function and a symbol. As a physical scientist I routinely work with equations where all the symbols can be thought of as functions of all the other symbols. Consider the ideal gas law: P = nRT/V, where P = pressure, n = moles, R = gas constant, T = absolute temperature and V = volume. You can solve this equation for any of the symbols. In the form it is written it makes perfect sense to say dP/dT = nR/V. If I had solved for V => dV/dT = nR/P. Measurements in the laboratory show that both relationships are true. Thus the following does not make sense for this simple example:
>>>diff(P=n*R*T/V,T)
0 = n*R/V
n*R/V is generally not equal to zero (only two cases: n = 0 and V = oo; both physically not very important).

Because of the way SymPy presently treats functions, getting the behavior that I would expect as a scientists for an equation such as the ideal gas law cannot be achieved by using functions instead of symbols.

The fundamental question, as Oscar points out, is whether we want to be able to wrap an equation in an operation and have that default to application of the operation to both sides. We are clear that this should work for simple binary math operations, but for other things it is less clear.

From an ease of use and consistency with how expressions behave, I am inclined towards being able to wrap equations in operations such as `cos(eqn)`. However, there are clearly some cases where this can cause problems. So, two important questions we would like the community to respond to:
  1. How important is the convenience of being able to wrap equations in operations?
  2. What operations are you aware of where this behavior could cause problems?

Aaron Meurer

unread,
Jan 20, 2021, 10:29:13 PM1/20/21
to sympy
On Wed, Jan 20, 2021 at 3:49 PM gu...@uwosh.edu <gu...@uwosh.edu> wrote:
>
> First, Oscar thank you for taking the time to summarize for everyone the key issues we are trying to make decisions on.
>
> Second, I think I can help people understand a little more about why these are issues.
>
> Most of them arise because in code we make a distinction between a function and a symbol. As a physical scientist I routinely work with equations where all the symbols can be thought of as functions of all the other symbols. Consider the ideal gas law: P = nRT/V, where P = pressure, n = moles, R = gas constant, T = absolute temperature and V = volume. You can solve this equation for any of the symbols. In the form it is written it makes perfect sense to say dP/dT = nR/V. If I had solved for V => dV/dT = nR/P. Measurements in the laboratory show that both relationships are true. Thus the following does not make sense for this simple example:
> >>>diff(P=n*R*T/V,T)
> 0 = n*R/V
> n*R/V is generally not equal to zero (only two cases: n = 0 and V = oo; both physically not very important).
>
> Because of the way SymPy presently treats functions, getting the behavior that I would expect as a scientists for an equation such as the ideal gas law cannot be achieved by using functions instead of symbols.

This starts to get into the question of what the semantic meaning of
Eqn is. Should Derivative treat the variables in Eqn as depending on
each other, because the equality implies they do? What about Eqn(f(x),
a*x)? Does the presence of f(x) prevent a from depending on x? You
could also write Eqn(a, f(x)/x). The first one "looks" like a doesn't
depend on x and the second one "looks" like it does. But shouldn't the
two Eqns be treated in the same way, since one can be obtained from
the other by a simple, reversible transformation?

My feeling on this is that Derivative already solves this problem by
implicitly assuming that variables are independent of one another,
unless explicitly stated otherwise via a functional relationship. I
don't think Eqn should change this behavior. As an aside, it may be
useful, independently of Eqn, to have a function that can take
derivatives under the assumption that certain symbols depend on one
another, as a convenience over creating explicit Function objects
(idiff() is an example of this sort of thing).

But the underlying question about the semantic meaning is, to what
degree does Eqn represent an "equality", vs. just being a convenient
way to manipulate two expressions in tandem. This question also comes
up with some of my suggestions about manipulating an Eqn in my
previous post. I think an Eqn *is* more than just a pair of
expressions. But we should think about a clear way to define an
"equality" so that it is clearer what an Eqn should and shouldn't do.

>
> The fundamental question, as Oscar points out, is whether we want to be able to wrap an equation in an operation and have that default to application of the operation to both sides. We are clear that this should work for simple binary math operations, but for other things it is less clear.
>
> From an ease of use and consistency with how expressions behave, I am inclined towards being able to wrap equations in operations such as `cos(eqn)`. However, there are clearly some cases where this can cause problems. So, two important questions we would like the community to respond to:

This really boils down to how we want to answer the question I posed above.

I think that ideally cos(eq) would work. If it works with operations
like + and *, it should work with any mathematical function. I also
think Eqn should be an Expr so that it has all the same methods.

The problem is how to make this work technically. As Oscar said, you
can get most mathematical "functions" in SymPy by hooking into
Function. But that doesn't do anything for manipulation or
simplification functions, like simplify(). Ideally, any generic
function that doesn't know about Eqn would just operate on both sides
of the equation, and so long as it is a function that normally takes
an expression and returns an expression, it should work. But how do we
make this work technically? Does every function need to be decorated
with some special logic to pass through equations? The ability to
"pass through" or dispatch on arbitrary functions is a larger question
that has come up in a lot of other places in SymPy. I don't know if
we have a solution for it yet, but it is something we'd like to solve.

Aaron Meurer
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/d7f67a32-381b-4ca1-baa0-da6c240c4c98n%40googlegroups.com.

David Bailey

unread,
Jan 21, 2021, 12:40:01 PM1/21/21
to sy...@googlegroups.com
On 20/01/2021 13:54, Oscar Benjamin wrote:
I think David is using Windows so that apt-get command which is for
Debian Linux can not be used.

David I thought that the standard Python installers for Windows would
mean that you had tkinter installed. I don't think you can install it
with pip because it's basically part of Python (so not really a
"separate" package). There are many different ways of installing
Python though so it might depend on exactly which installer you used.

I would generally use the installer files from here:
https://www.python.org/downloads/

Yes that contains tkinter, but I have discovered that matplotlib won't install on 3.9.1!

There is probably nothing you can suggest, so I suppose I am just venting my frustration!

David

Aaron Meurer

unread,
Jan 21, 2021, 2:51:26 PM1/21/21
to sympy
My general recommendation for these packaging problems is to use
Anaconda/conda-forge, especially if you are on Windows.

Aaron Meurer
> --
> 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 sympy+un...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/fa129096-313e-da39-1f7f-bfe39215553d%40dbailey.co.uk.

Oscar Benjamin

unread,
Jan 21, 2021, 3:21:03 PM1/21/21
to sympy
There is not usually any problem installing common packages like
matplotlib with pip any more. Perhaps this is related to the problems
with the recent numpy release on Windows.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/CAKgW%3D6J7sW36_2D%3DBuTCRhN5ApHqC0vvFMxhW9%2Bu7xjCU7NHQw%40mail.gmail.com.

Matthias Geier

unread,
Feb 4, 2021, 4:18:48 PM2/4/21
to sy...@googlegroups.com
Hi all.

First of all, thanks for all the effort all of you are putting into
SymPy in general and into the upcoming Equation class specifically!

I'm not sure whether I fully understand either side of the contentious
questions mentioned in this thread, but let me nevertheless add my
opinion:

I think what Jonathan describes is a mixture of two things:

* an "equation" where operations can be applied on both sides
* a "definition" or "assignment" where an expression (on the right
side) gets a new name (on the left side)

I'm quite sure that those should be two separate classes, but I'm not
sure whether *both* should be part of SymPy.

I think the "Equation" class should never treat the two sides
differently (unless explicitly requested by "rhs" or "lhs" stuff, and
probably as argument to subs()?).

However, I can very much appreciate the desire to have a symbol that
represents an expression, where both are displayed nicely with an
equals sign in between.

FWIW, I've somewhat recently created an ad-hoc class called
"NamedExpression" that does some of what Jonathan describes.

It's defined there:
https://github.com/AudioSceneDescriptionFormat/splines/blob/master/doc/euclidean/utility.py

And there's an example using it:
https://splines.readthedocs.io/en/latest/euclidean/hermite-uniform.html

Apart from derivatives, there is also .evaluate_at() which treats the
two sides of the equation differently.

On top of that, I've also made a "NamedMatrix" class with a few
additional features specific to matrices.
For example it allows to invert and transpose both sides (which is
also shown in the example mentioned above).

As a special feature it has also a .pull_out() method, which allows to
pull a common factor out of all matrix coefficients.

I'm not suggesting to implement any of this in SymPy, probably it is
much too specific and uses some assumptions that are not appropriate
for general use.

Probably something like this could become a separate Python module
(targeted at teaching) using building blocks from SymPy (like the
upcoming Equation class)?

cheers,
Matthias
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/CAKgW%3D6KSj3NTWDgqr7WjeTFf%3DkHrAtvk7sxkW%2Bzm_RbLO72ZWg%40mail.gmail.com.

Aaron Meurer

unread,
Feb 4, 2021, 5:51:31 PM2/4/21
to sympy
On Thu, Feb 4, 2021 at 2:18 PM Matthias Geier <matthia...@gmail.com> wrote:
>
> Hi all.
>
> First of all, thanks for all the effort all of you are putting into
> SymPy in general and into the upcoming Equation class specifically!
>
> I'm not sure whether I fully understand either side of the contentious
> questions mentioned in this thread, but let me nevertheless add my
> opinion:
>
> I think what Jonathan describes is a mixture of two things:
>
> * an "equation" where operations can be applied on both sides
> * a "definition" or "assignment" where an expression (on the right
> side) gets a new name (on the left side)
>
> I'm quite sure that those should be two separate classes, but I'm not
> sure whether *both* should be part of SymPy.
>
> I think the "Equation" class should never treat the two sides
> differently (unless explicitly requested by "rhs" or "lhs" stuff, and
> probably as argument to subs()?).

I agree with this.

Incidentally, there is an Assignment class in SymPy, in the codegen
module. It is designed specifically around generating code, so it
might not apply to the use cases here.

Aaron Meurer
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/CAFesC-c1SzDeVd%3D5iz%3Dm6dRUWs2Y24%2Bpw-6g6%3DK4KoEG7T5cJg%40mail.gmail.com.

gu...@uwosh.edu

unread,
Feb 4, 2021, 9:35:47 PM2/4/21
to sympy
I think the issue is whether we are thinking about this as computer code or a computer tool for doing mathematics.

I believe the mathematical concept of an equation that connects two expressions with an equals sign also implies an assignment. When you write `a = b/c` on a piece of paper the equation implies: `a==b/c` assuming no mistake is made. It implies you can calculate a by taking b and dividing it by c. It implies that you can rearrange it to calculate b or c from the other two variable.  It also implies: `da/db = 1/c` and so on. I see little value to the equation class if it simply is the equivalent of a list of two expressions that operations are distributed over. I think SymPy pretty much covers that behavior with vectors or just Python lists.

I have also made some more elaborate comments in the draft SymPEP for the equation class. But in summary they point out that if we apply operations such as diff() the same to both sides of an equation we arrive at expressions that are not consistent in a general way with the application of derivatives to an equation.

An equation class that behaves as a list of two expressions which is displayed with an equals sign between the two expressions is certainly easier to implement. However, IMHO an equation with an equals sign is more than that.

Jonathan

gu...@uwosh.edu

unread,
Feb 5, 2021, 10:07:55 PM2/5/21
to sympy
As the number of different people responding to this discussion has been small I have put together the following simple 2 question survey about expectations/preferences for differentiation of equations to try to broaden the input. This is a simple google form. It collects google addresses only to avoid multiple responses from the same google account. I expect it to take people about 1 minute. The link is: https://docs.google.com/forms/d/e/1FAIpQLSd-VCuOuNQtEo8VAuDA42SgNPYhMhr4Z5nEsGdwYoG9SCI5Nw/viewform?usp=sf_link.

Thanks,
Jonathan

Matthias Geier

unread,
Feb 6, 2021, 6:23:04 AM2/6/21
to sy...@googlegroups.com
On Fri, Feb 5, 2021 at 3:35 AM gu...@uwosh.edu wrote:

[...]

> I believe the mathematical concept of an equation that connects two expressions with an equals sign also implies an assignment.

I think that very much depends on the conventions of the field you are
working in.

In case of doubt I would probably use `:=` for assignment.

> When you write `a = b/c` on a piece of paper the equation implies: `a==b/c` assuming no mistake is made.

I would say both mean the same, once written in a "math" style and
once in a "programming" style.

In this case, `b/c = a` means exactly the same (as does `b/c==a`),
which is a hint that this is in fact not an assignment.

> It implies you can calculate a by taking b and dividing it by c. It implies that you can rearrange it to calculate b or c from the other two variable.

I guess there are some limitations depending on what type of things
those symbols represent, but in many cases you should be able to
rearrange them.

> It also implies: `da/db = 1/c` and so on.

Only if `c` doesn't represent a sub-expression containing `b`.
An on the other hand, if `a` doesn't represent such a sub-expression,
the LHS will be 0.
Which I think is the crux of this discussion.

I think it would work as expected if you defined your symbols like this:

import sympy as sp

b, c = sp.symbols('b c')
a = sp.Function('a')(b)

> I see little value to the equation class if it simply is the equivalent of a list of two expressions that operations are distributed over. I think SymPy pretty much covers that behavior with vectors or just Python lists.

I'm not sure about Python lists, but it would indeed be very similar
to a sp.Matrix with two elements.

There may or may not be additional functionality, but I think the main
feature would be its display.
It would be displayed like an equation in a text book.
And I think this is very valuable.

And if its behavior is nicely generic and consistent, it can probably
be used as a building block for more specialized custom helper
classes/functions/etc.

> I have also made some more elaborate comments in the draft SymPEP for the equation class. But in summary they point out that if we apply operations such as diff() the same to both sides of an equation we arrive at expressions that are not consistent in a general way with the application of derivatives to an equation.

Does it work with the definition I showed above, using sp.Function?

> An equation class that behaves as a list of two expressions which is displayed with an equals sign between the two expressions is certainly easier to implement. However, IMHO an equation with an equals sign is more than that.

That's why in my previous post I was suggesting to create two separate classes.
One for the "equation" aspect (the simpler the better!), and one
(derived from Equation) for whatever additional behavior is needed.

cheers,
Matthias
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/979503cf-84e1-4d58-b939-bde8b055c36an%40googlegroups.com.

David Bailey

unread,
Feb 6, 2021, 12:16:23 PM2/6/21
to sy...@googlegroups.com
On 06/02/2021 03:07, gu...@uwosh.edu wrote:
As the number of different people responding to this discussion has been small I have put together the following simple 2 question survey about expectations/preferences for differentiation of equations to try to broaden the input. This is a simple google form. It collects google addresses only to avoid multiple responses from the same google account. I expect it to take people about 1 minute. The link is: https://docs.google.com/forms/d/e/1FAIpQLSd-VCuOuNQtEo8VAuDA42SgNPYhMhr4Z5nEsGdwYoG9SCI5Nw/viewform?usp=sf_link.

I am sorry to be awkward, but I found this tricky to explain on the one line provided for 'Other'.

The definition of an equation seems so important (and remarkably tricky!), would it be better to implement something that seems reasonable and put it out as experimental code?

Another possibility might be to simply fault any attempt to take the derivative of an equation (and no doubt some other awkward operations) until people had had time to experiment with equations for a bit.

For example, if I chose option 5, I'd want to figure out how easy it would be to set all the unwanted derivatives to zero, or stop them appearing.

David

gu...@uwosh.edu

unread,
Feb 6, 2021, 4:45:28 PM2/6/21
to sympy
David,

No problem at all! Thanks for the feedback. It is very useful. We'll see what happens with the simple survey, but up to this point each one of those five options had been a preference for 1 or 2 individuals. So there isn't or wasn't any overwhelming preference. Unless a strong consensus develops, I am thinking along much the same lines you indicate in your last two sentences:
  • `diff(Eqn)` would raise an error (ValueError?) that would say the operation can only be applied to one side or the other with example syntax. The error would also direct the user to a special method (`ediff()`?).
  • `ediff(Eqn, wrt_list, const_list)` Eqn: the equation, wrt_list: symbols to differentiate wrt, const_list: symbols to hold constant. So this would be a fully specified partial derivative.
  • There is still the problem with how any derivatives in an equation would end up being evaluated. With SymPy's present capabilities there would need to be some autogeneration of functions to make this work with the differential equations module.
  • One stumbling block to bringing all of this together is that SymPy does not contain the concept of an infinitesimal of a symbol. I have not looked at it carefully, but some of the work necessary for this may be included in the differential geometry module. It is certainly embedded as an assumption in the integration module.
Anyway. Thanks for the feedback. I will let the list know what the responses are. I'll probably wait a few more days to make sure people have a chance to respond.

Jonathan

Oscar Benjamin

unread,
Feb 6, 2021, 5:31:48 PM2/6/21
to sympy
Hi Jonathan,

I just looked at the survey and I think the question as posed is
something of a distraction from the real issue. If I set that as an
exercise to students then it would be a trick question. The correct
answer is "it depends on information not given" :)

I've set this to students many times. Is the following a linear function?

f = ax^2 + bx + c

I get the expected "no, it's nonlinear" and then I point out that I
haven't actually specified what variables f is supposed to be a
function of. If that's f(x) then it's nonlinear. If it's f(a, b, c)
then it's linear. There is a practical usage of this distinction which
is that in polynomial fitting I know values `(f1, x1)`, `(f2, x2)` etc
and I want to find the coefficients `a, b, c`: I can do that by
solving linear equations. On the other hand if I know `a, b, c` and
`f1` and I want `x1` then the same equation defines a nonlinear
problem (roots of a polynomial).

The question in the survey boils down to "what do you think df/dx
should be" except you've specified nothing about f! My answer for what
my preference would be is that each of those possible results is valid
*in different situations*. Ideally I would be able to do *all* of
those things in sympy but it's not possible to support all of those
without me as a user supplying some information to specify what it is
I want sympy to do: I need to say which variables depend on which
others.

Many people depend on the fact that differentiating a symbol with
respect to another symbol treats that symbol as a constant: that's a
long-standing feature in sympy and in most situations it is precisely
what is wanted. It would be great to extend sympy's capabilities in
terms of understanding mutual dependence between symbols though.
Perhaps there could be a function to create interdependent symbols
like:

P, V, T = interdependent_symbols('P, V, T')

The implication would be that any of P, V or T can be considered to be
a function of the other two like P(V, T). If we had that then we could
express notions like (dP/dV)_T (derivative of P wrt V at constant T).
That's something that a lot of people want and I would also like to
have it.

While I completely agree with the issue that you have identified this
is not a problem to be solved in an Equation class. The Equation class
should not bestow special behaviour on the expressions that they would
not have in other contexts. The different behaviours you ask about in
the survey should be achievable by defining f, a, b, c and x in
different ways. The result of differentiating the equation should
always be the same as differentiating the expressions on each side
though: anything else would lead to bugs, confusion and a never-ending
stream of complaints.

Oscar
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/7d048bd4-47ad-471e-bef5-35bb42b58c24n%40googlegroups.com.

David Bailey

unread,
Feb 6, 2021, 7:07:19 PM2/6/21
to sy...@googlegroups.com

Dear Group,

While thinking about Jonathon's question, I came across this oddity:

x=symbols('x')

f=symbols('f',cls=Function)

diff(f,x)

1

Why 1? I think I would have expected it to generate a TypeError, just like f+x does.

David



Aaron Meurer

unread,
Feb 6, 2021, 11:58:24 PM2/6/21
to sympy
I agree it should be an error. SymPy is getting better at checking types of inputs, but in general, you can expect a garbage in, garbage out behavior.

Aaron Meurer

--
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 sympy+un...@googlegroups.com.

Oscar Benjamin

unread,
Feb 7, 2021, 6:29:58 AM2/7/21
to sympy
The problem is that an undefined function like f is actually a class.
An object like f(x) is an instance of that class. When you do diff(f,
x) that calls through to f.diff(x). The f class has a diff method but
it is supposed to be called with an instance. Instead if you just call
f.diff(x) then the diff method is called with x as the instance so
what you get is equivalent to x.diff() which is treated as x.diff(x)
which gives 1.

It's like this:

In [10]: class MyExpr(Expr):
...: def diff(self, *symbols):
...: print('self =', self)
...: print('symbols =', symbols)
...:

In [11]: diff(MyExpr, x)
self = x
symbols = ()

In [12]: diff(MyExpr(y), x)
self = MyExpr(y)
symbols = (x,)

Ideally we should change it so that an undefined function like f is a
Basic instance rather than a Basic subclass.


Oscar

S.Y. Lee

unread,
Feb 9, 2021, 6:58:51 PM2/9/21
to sympy
I was not very fond for defining sympy Add, Mul, Pow or Function application for equation object because I don't think that the algebra of equation looks well unifiable with other mathematical objects like Expr at the first glance,
And I think that it's important to introduce Add, Mul, Pow, or function application for Expr or at least the mathematical objects that are conceptually unifiable with Expr.
We automatically arrive to a conclusion that we need to define unevaluated sympy functions like `Add(eq1, eq2, evaluate=False)`, `sin(eq, evaluate=False)`, once we define how to define function application for them.

And we would also arrive in questions like: If equation brings its own algebra system, there should be an equation of equations? How should we solve them?

Oscar Benjamin

unread,
Feb 9, 2021, 7:54:07 PM2/9/21
to sympy
On Tue, 9 Feb 2021 at 23:58, S.Y. Lee <syle...@gmail.com> wrote:
>
> I was not very fond for defining sympy Add, Mul, Pow or Function application for equation object because I don't think that the algebra of equation looks well unifiable with other mathematical objects like Expr at the first glance,
> And I think that it's important to introduce Add, Mul, Pow, or function application for Expr or at least the mathematical objects that are conceptually unifiable with Expr.
> We automatically arrive to a conclusion that we need to define unevaluated sympy functions like `Add(eq1, eq2, evaluate=False)`, `sin(eq, evaluate=False)`, once we define how to define function application for them.

The suggestion is not to define Add, Mul or Pow for equation
arguments. The suggestion is that the Python operators +, *, ** can be
used with equations. They should never result in an unevaluated Add
though and I would expect Add(eq1, eq2) to raise an error. One of the
contentious parts of the proposal is being able to do Function(eq)
(e.g. cos(eq)) and that part I also object to.

There are many different classes in sympy that use operators like +, *
etc without being Expr.

> And we would also arrive in questions like: If equation brings its own algebra system, there should be an equation of equations? How should we solve them?

It's not an "algebra system": it's just a few convenience operations.
It should not be confused with allowing Equation to be used in places
where Expr is expected.

--
Oscar

David Bailey

unread,
Feb 10, 2021, 8:47:03 AM2/10/21
to sy...@googlegroups.com

Might it be an idea to introduce functions such as AddSides, SubtractSides, etc that Mathematica has? Not only would these avoid any confusion with the normal arithmetic operations,  but I think using these functions would clarify what is going on. After all, when people do such operations by hand, they don't write (a=b)*k,or whatever, they typically write "Multiplying equation 4 by k we get..."

David

Aaron Meurer

unread,
Feb 10, 2021, 3:24:42 PM2/10/21
to sympy
Is there a need to have an unevaluated version of AddSides? + already works for an evaluated version.

It might be worth thinking about inequalities in the design here. We've been ignoring them because it's simpler to do equalities first, but inequalities have some restrictions that might clarify what design we want. For a > b, things like function application or even multiplication aren't straightforward. What should x*(a > b) do if x is only known to be real? One option is a Piecewise, but then things get hairy if you start doing other operations. Maybe a MultiplySides does actually make sense for this.

Aaron Meurer

David

--
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 sympy+un...@googlegroups.com.

Oscar Benjamin

unread,
Feb 10, 2021, 3:44:22 PM2/10/21
to sympy
On Wed, 10 Feb 2021 at 20:24, Aaron Meurer <asme...@gmail.com> wrote:
>
> On Wed, Feb 10, 2021 at 6:47 AM David Bailey <da...@dbailey.co.uk> wrote:
>>
>> On 10/02/2021 00:53, Oscar Benjamin wrote:
>>
>> On Tue, 9 Feb 2021 at 23:58, S.Y. Lee <syle...@gmail.com> wrote:
>>
>>
>> And we would also arrive in questions like: If equation brings its own algebra system, there should be an equation of equations? How should we solve them?
>>
>> It's not an "algebra system": it's just a few convenience operations.
>> It should not be confused with allowing Equation to be used in places
>> where Expr is expected.
>>
>> Might it be an idea to introduce functions such as AddSides, SubtractSides, etc that Mathematica has? Not only would these avoid any confusion with the normal arithmetic operations, but I think using these functions would clarify what is going on. After all, when people do such operations by hand, they don't write (a=b)*k,or whatever, they typically write "Multiplying equation 4 by k we get..."
>
>
> Is there a need to have an unevaluated version of AddSides? + already works for an evaluated version.
>
> It might be worth thinking about inequalities in the design here. We've been ignoring them because it's simpler to do equalities first, but inequalities have some restrictions that might clarify what design we want. For a > b, things like function application or even multiplication aren't straightforward. What should x*(a > b) do if x is only known to be real? One option is a Piecewise, but then things get hairy if you start doing other operations. Maybe a MultiplySides does actually make sense for this.

See also these two links:
https://github.com/sympy/sympy/pull/20723#issuecomment-763975854
https://github.com/sympy/sympy/pull/17097

Yes, DivideSides would make sense for unevaluated division of inequalities etc.

That is not inconsistent with using + though: We can use eq1+eq2 as a
shorthand for the evaluated form of AddSides(eq1, eq2). For equations
that would always be able to evaluate. In Mathematica this is all
organised around making Boolean expressions that can evaluate after
substitution.


Oscar

Aaron Meurer

unread,
Feb 10, 2021, 4:58:21 PM2/10/21
to sympy
We can generalize this to applying any function to equations or inequalities. For equations, it matters where the function either isn't defined (like y=0 for f(x, y) = x/y), or isn't well-defined (for example, square roots are multivalued). For inequalities it matters on what parts of the domain the function is (strictly) monotonic. Except I don't know if SymPy can really answer either of these questions right now. So this might have to remain only a theoretical idea for the time being.

Aaron Meurer
 


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 sympy+un...@googlegroups.com.

gu...@uwosh.edu

unread,
Feb 10, 2021, 9:05:13 PM2/10/21
to sympy
I'm swamped with grading and class preparation, but wanted to comment briefly on the items below:

On Wednesday, February 10, 2021 at 3:58:21 PM UTC-6 asme...@gmail.com wrote:
On Wed, Feb 10, 2021 at 1:44 PM Oscar Benjamin <oscar.j....@gmail.com> wrote:

Yes, DivideSides would make sense for unevaluated division of inequalities etc.

That is not inconsistent with using + though: We can use eq1+eq2 as a
shorthand for the evaluated form of AddSides(eq1, eq2). For equations
that would always be able to evaluate. In Mathematica this is all
organised around making Boolean expressions that can evaluate after
substitution.

We can generalize this to applying any function to equations or inequalities. For equations, it matters where the function either isn't defined (like y=0 for f(x, y) = x/y), or isn't well-defined (for example, square roots are multivalued). For inequalities it matters on what parts of the domain the function is (strictly) monotonic. Except I don't know if SymPy can really answer either of these questions right now. So this might have to remain only a theoretical idea for the time being.

Aaron Meurer
 

Oscar
I don't see a problem with returning multivalued/multiple equation results. If I understand what you are talking about for expressions where you have an equal sign this is working reasonably in the present implementation. For example a simplified example of a quantum problem my students just did:
Screenshot from 2021-02-10 19-58-41.png
The right hand size is of type `Piecewise`.
Jonathan

Aaron Meurer

unread,
Feb 11, 2021, 3:44:22 PM2/11/21
to sympy
How would you handle dividing both sides of an equation by something? I don't know if a piecewise makes sense. (a = b)/x would be "a/x = b/x if x != 0, ??? otherwise". What meaningful thing could the ??? be? You can easily manipulate an equation into nonsense if you aren't careful about this (just Google "proof that 1 = 2"). But I don't know if SymPy should try to take responsibility to prevent it. 

Aaron Meurer
 
Jonathan

--
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 sympy+un...@googlegroups.com.

David Bailey

unread,
Feb 11, 2021, 4:43:49 PM2/11/21
to sy...@googlegroups.com
On 11/02/2021 20:44, Aaron Meurer wrote:


How would you handle dividing both sides of an equation by something? I don't know if a piecewise makes sense. (a = b)/x would be "a/x = b/x if x != 0, ??? otherwise". What meaningful thing could the ??? be? You can easily manipulate an equation into nonsense if you aren't careful about this (just Google "proof that 1 = 2"). But I don't know if SymPy should try to take responsibility to prevent it. 

This is exactly why I think it would be good to give the users DivideSides, and MultiplySides, which they could use as they saw
appropriate, rather than eqn(a,b)/c, etc.  SymPy would not take responsibility for this mathematical operation. To me, this is exactly analogous to subs:

cos(x).subs(cos,sin)

sin(x)

SymPy does not guarantee that this is valid!

Also there is absolutely no sense in which eqn(a,b) is twice as big as eqn(a,b)/2, so is it desirable that the notation should suggest that it is?

David



Chris Smith

unread,
Mar 12, 2021, 12:30:41 PM3/12/21
to sympy
> Add(eq1, eq2) to raise an error

What is wrong with `Eqn(eq1.lhs+eq2.lhs, eq1.rhs+eq2.rhs)`?

/c

Reply all
Reply to author
Forward
0 new messages