small questions about manipulating equations and sums

36 visualizações
Pular para a primeira mensagem não lida

Thomas Ligon

não lida,
30 de abr. de 2020, 11:18:4430/04/2020
para sympy
I have a few small questions where I can solve the issues manually and have the feeling that in my attempts to solve them with SymPy I am overlooking something. The examples below are trivial and easy to solve manually, but the project I am working on involves much more complex expressions where it would be great to have a better solution in SymPy.
Basically, adding two equations does not produce the desired solution, and sums do not expand or simplify as expected.
PS. The print('end') statement is just there as a convenient place for a breakpoint in my debugger.

from sympy import symbols, Eq, expand, simplify, latex, oo, Sum
x, y, p, q, a, j, Aj, Bj = symbols('x y p q a j A_j B_j')
eq1 = Eq(x, y)
print(latex(eq1)) # OK
eq2 = Eq(p, q)
print(latex(eq2)) # OK
eq3 = eq1 + eq2
print(latex(eq3)) # not what I want
eq4 = Eq(eq1.lhs + eq2.lhs, eq1.rhs + eq2.rhs)
print(latex(eq4)) # yes, what I want, but tedious
eq5 = a*eq1
print(latex(eq5)) # not exactly what I want
eq6 = Eq(a*eq1.lhs, a*eq1.rhs)
print(latex(eq6)) # yes, what I want, but tedious
ex1 = Sum(Aj + Bj, (j, -oo, oo))
print(latex(ex1)) # OK
ex2 = Sum(Aj - Bj, (j, -oo, oo))
print(latex(ex2)) # OK
ex3 = ex1 + ex2
print(latex(ex3)) # correct, but needs consolidation
ex4 = expand(ex3)
print(latex(ex4)) # latex looks bad
ex5 = simplify(ex3)
print(latex(ex5)) # latex completely wrong
print('end')
sumTest.py

Oscar Benjamin

não lida,
30 de abr. de 2020, 13:43:2830/04/2020
para sympy
What version of sympy are you using? I get an exception from eq3 = eq1 + eq2.

We need a new Equation class that works properly with arithmetic
operations etc. I have made written one but haven't added it to sympy
yet. I got sidetracked at the time because deeper structural changes
were needed like ensuring that Relational does not subclass Expr.

Here's my new Equation class which supports arithmetic operations:

from sympy.core.basic import Basic
from sympy.core.sympify import _sympify
from sympy.simplify import simplify
from sympy.core.relational import Equality

class Equation(Basic):

def __new__(cls, lhs, rhs):
lhs = _sympify(lhs)
rhs = _sympify(rhs)
return Basic.__new__(cls, lhs, rhs)

@property
def lhs(self):
return self.args[0]

@property
def rhs(self):
return self.args[1]

def as_Boolean(self):
return Equality(self.lhs, self.rhs)

@property
def reversed(self):
return Equation(self.rhs, self.lhs)

def applyfunc(self, func):
return Equation(func(self.lhs), func(self.rhs))

def applylhs(self, func):
return Equation(func(self.lhs), self.rhs)

def applyrhs(self, func):
return Equation(self.lhs, func(self.rhs))

@classmethod
def _binary_op(cls, a, b, opfunc_ab):
if isinstance(a, Equation) and not isinstance(b, Equation):
return Equation(opfunc_ab(a.lhs, b), opfunc_ab(a.rhs, b))
elif isinstance(b, Equation) and not isinstance(a, Equation):
return Equation(opfunc_ab(a, b.lhs), opfunc_ab(a, b.rhs))
elif isinstance(a, Equation) and isinstance(b, Equation):
return Equation(opfunc_ab(a.lhs, b.lhs), opfunc_ab(a.rhs, b.rhs))
else:
raise TypeError('One of a or b should be an equation')

def __add__(self, other):
return self._binary_op(self, other, lambda a, b: a + b)

def __radd__(self, other):
return self._binary_op(other, self, lambda a, b: a + b)

def __mul__(self, other):
return self._binary_op(self, other, lambda a, b: a * b)

def __rmul__(self, other):
return self._binary_op(other, self, lambda a, b: a * b)

def __sub__(self, other):
return self._binary_op(self, other, lambda a, b: a - b)

def __rsub__(self, other):
return self._binary_op(other, self, lambda a, b: a - b)

def __truediv__(self, other):
return self._binary_op(self, other, lambda a, b: a / b)

def __rtruediv__(self, other):
return self._binary_op(other, self, lambda a, b: a / b)

def __pow__(self, other):
return self._binary_op(self, other, lambda a, b: a ** b)

def __rpow__(self, other):
return self._binary_op(other, self, lambda a, b: a ** b)

def __str__(self):
return '%r = %r' % self.args

def expand(self, *args, **kwargs):
return self.func(*(arg.expand(*args, **kwargs) for arg in self.args))

def simplify(self, *args, **kwargs):
return simplify(self, *args, **kwargs)

def _eval_simplify(self, *args, **kwargs):
return self.func(*(arg.simplify(*args, **kwargs) for arg in self.args))

def factor(self, *args, **kwargs):
return factor(self, *args, **kwargs)

def evalf(self, *args, **kwargs):
return self.func(*(arg.evalf(*args, **kwargs) for arg in self.args))

def n(self, *args, **kwargs):
return self.func(*(arg.n(*args, **kwargs) for arg in self.args))

from sympy import symbols, cos, sin

x, y = symbols('x, y')
eq = Equation(x, y)
print(eq)
eq = Equation(1, 2)
print(eq)
eq = Equation(1, 2) + 3
print(eq)
eq = Equation(1, 2) + Equation(x, y)
print(eq)
eq = Equation(1, 2) - 3
print(eq)
eq = Equation(1, 2) - Equation(x, y)
print(eq)
eq = 1 - Equation(1, 2)
print(eq)
eq = 1 + Equation(x, y)
print(eq)

eq = Equation(cos(x)**2 + sin(x)**2 - 1, 2)


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/71f9f37a-1b10-4fe5-b7ef-65f92fe3bc86%40googlegroups.com.

Thomas Ligon

não lida,
30 de abr. de 2020, 13:51:1530/04/2020
para sympy
I am using SymPy 1.5.1. My package manager shows that as the newest.
> To unsubscribe from this group and stop receiving emails from it, send an email to sy...@googlegroups.com.

Oscar Benjamin

não lida,
30 de abr. de 2020, 14:18:4930/04/2020
para sympy
Oh sorry, I got confused. In 1.6 (the next release) adding two
Equalitys will give a TypeError.

In [1]: Eq(x, y) + Eq(z, t)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-1-42ca3cdeb265> in <module>
----> 1 Eq(x, y) + Eq(z, t)

TypeError: unsupported operand type(s) for +: 'Equality' and 'Equality'


In 1.5 it just gives a nonsense object:

In [1]: Eq(x, y) + Eq(z, t)
Out[1]: (x = y) + (z = t)


With the Equation class I showed it adds the left and right hand sides:

In [3]: Equation(x, y) + Equation(z, t)
Out[3]: x + z = t + y

--
Oscar
> 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/a9a6e326-12c3-4838-8b5a-6cd5aa90c58d%40googlegroups.com.

Chris Smith

não lida,
30 de abr. de 2020, 17:26:4030/04/2020
para sympy
The following light-weight version of what Oscar has suggested might be sufficient:

>>> def add(s,o):
...   assert s.func == o.func
...   return s.func(s.lhs+o.lhs,o.rhs+s.rhs)
...
>>> def mul(s,o):
...   return s.func(o*s.lhs,o*s.rhs)
...
>>> def rmul(s,o):
...   return s.func(o*s.lhs,o*s.rhs)
...
>>> Eq.__add__ = add
>>> Eq.__mul__ = mul
>>> Eq.__rmul__ = rmul
>>>
>>> from sympy import symbols, Eq, expand, simplify, latex, oo, Sum
>>> x, y, p, q, a, j, Aj, Bj = symbols('x y p q a j A_j B_j')
>>> eq1 = Eq(x, y)
>>> eq2 = Eq(p, q)
>>> eq1 + eq2
Eq(p + x, q + y)
>>> a*eq1
Eq(a*x, a*y)


And for the Sums, use IndexedBase otherwise Sum doesn't know that the symbols depend on j

>>> Aj = IndexedBase("A")[j]>>> Bj = IndexedBase("B")[j]

>>> ex1 = Sum(Aj + Bj, (j, -oo, oo))
>>> ex2 = Sum(Aj - Bj, (j, -oo, oo))
>>> ex1 + ex2
Sum(A[j] - B[j], (j, -oo, oo)) + Sum(A[j] + B[j], (j, -oo, oo))
>>> expand(_)
2*Sum(A[j], (j, -oo, oo)) + Sum(-B[j], (j, -oo, oo)) + Sum(B[j], (j, -oo, oo))
>>> factor_terms(_)
2*Sum(A[j], (j, -oo, oo))



Oscar Benjamin

não lida,
30 de abr. de 2020, 17:40:1630/04/2020
para sympy
Seeing that use of factor_terms makes me think again that expand
should definitely pull out that constant.
> --
> 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/ae75c13d-d888-441f-9742-c3f8c3014506%40googlegroups.com.

David Bailey

não lida,
30 de abr. de 2020, 18:29:4730/04/2020
para sy...@googlegroups.com
On 30/04/2020 19:18, Oscar Benjamin wrote:
In 1.5 it just gives a nonsense object:

In [1]: Eq(x, y) + Eq(z, t)
Out[1]: (x = y) + (z = t)

However, it should surely be valid to add an equation to an expression:

>>> Eq(a-3,b-3)+3

3 + Eq(a-3,b-3)

>>> simplify(3 + Eq(a - 3, b - 3))

3 + Eq(a - 3, b - 3)

Obviously I thought that would produce Eq(a,b)

At 1.5 the addition happens but the result doesn't simplify in the obvious way. Is there a rational to that? From what you say, I suspect this construct will also be banned at 1.6, but it seems perfectly meaningful.

David

Oscar Benjamin

não lida,
30 de abr. de 2020, 18:54:3530/04/2020
para sympy
Hi David,

I agree entirely. Neither gives a sensible result in 1.5 and both will
give errors in 1.6.

The Equation class I showed handles all of these though:

In [27]: x, y = symbols('x, y', real=True)

In [28]: eq = Equation(x, y)

In [29]: eq
Out[29]: x = y

In [30]: eq + 1
Out[30]: x + 1 = y + 1

In [31]: eq + eq
Out[31]: 2*x = 2*y

In [32]: eq1 = Equation(exp(I*x), cos(x)+I*sin(x))

In [33]: eq1
Out[33]: exp(I*x) = I*sin(x) + cos(x)

In [34]: eq2 = eq1.applyfunc(conjugate)

In [35]: eq2
Out[35]: exp(-I*x) = -I*sin(x) + cos(x)

In [36]: (eq1 - eq2)/2
Out[36]: exp(I*x)/2 - exp(-I*x)/2 = I*sin(x)

In [37]: (eq1 + eq2)/2
Out[37]: exp(I*x)/2 + exp(-I*x)/2 = cos(x)

--
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/645e3ca5-267d-5368-b0e8-9837a7e47598%40dbailey.co.uk.

Aaron Meurer

não lida,
30 de abr. de 2020, 19:15:0630/04/2020
para sympy
To clarify the rationale of it being an error in 1.6, you should think
of Eq as being a boolean type, that is, any Eq is either True or
False. Doing arithmetic operations like + or * on booleans doesn't
make sense. Rather, it should be used in boolean contexts like the
condition of a Piecewise or in an And or Or. The Equation class which
Oscar intends on adding will act like more an expression, so that
arithmetic operations make sense on it.

More practically, in 1.5, Eq + Eq produces a result, but the result is
complete nonsense. It only works because SymPy creates Add(Eq, Eq),
but such a thing doesn't actually work in any function you would pass
it to. A better model would be Equation, for which common operations
would produce another Equation, and you want to pass it to a function
that doesn't support Equation, you should pass the lhs and rhs
separately. I think it would still be possible to have an Equation
class nested inside of an expression, but such things should be
avoided (or maybe we should try to disallow it).

Aaron Meurer
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/CAHVvXxQS4XeNKUOhVx-wr_wh_gs0b61xjXF9KWxRpDeYgdUs_A%40mail.gmail.com.

David Bailey

não lida,
30 de abr. de 2020, 19:15:4630/04/2020
para sy...@googlegroups.com
On 30/04/2020 23:54, Oscar Benjamin wrote:
Hi David,

I agree entirely. Neither gives a sensible result in 1.5 and both will
give errors in 1.6.

The Equation class I showed handles all of these though:

So I guess since you say you haven't been able to put it in SymPy yet, the Equation class won't be going in 1.6, but maybe 1.7?

David

Oscar Benjamin

não lida,
30 de abr. de 2020, 19:20:5030/04/2020
para sympy
Hi David,

Yes, I hope something like it would go into 1.7. I don't have time
myself to make it ready for 1.6.

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/8a09f4f6-d82a-4ec8-905b-2d45758b7992%40dbailey.co.uk.

Oscar Benjamin

não lida,
30 de abr. de 2020, 19:33:0530/04/2020
para sympy
Hi Aaron,

Yes, if we had Equation we should disallow its use in an expression. I
don't see any situation where it would make sense. We would need a
clear distinction between Equation and Equality as well as methods to
convert between them.

1. Equality is a Boolean
2. Equation is Basic
3. Neither is Expr
4. You can use `.as_Boolean/.as_Equation` or something to convert between
5. Equation should be used instead of Equality for e.g. passing
equations to solve/dsolve and for returning solutions.
6. Equation should be invalid in any Boolean context
7. Equality should not support any of the arithmetic manipulations
that Equation does
8. Equation should be usable in subs or subseq or something (but
Equality should not)
9. Equation should have a solve method for making something the lhs
10. Documentation should find a way of making this understandable...

--
Oscar
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/CAKgW%3D6KspoZG4Qxfw%2Bd3XShoKEqyUn9E8oJkEEqktXpxZqxE7A%40mail.gmail.com.

Thomas Ligon

não lida,
1 de mai. de 2020, 06:44:4301/05/2020
para sympy
Do you want me to open issues on GitHub? This is actually two topic (equations and sums), an I should have known better than to combine them.

A mensagem foi excluída

Chris Smith

não lida,
1 de mai. de 2020, 07:02:2301/05/2020
para sympy
https://github.com/sympy/sympy/pull/18174 is open for the ability to work with equations and https://github.com/sympy/sympy/issues/19191 for extracting constants/sign from Integral/Sum.

David Bailey

não lida,
1 de mai. de 2020, 07:09:5001/05/2020
para sy...@googlegroups.com
On 01/05/2020 00:32, Oscar Benjamin wrote:
Hi Aaron,

Yes, if we had Equation we should disallow its use in an expression. I
don't see any situation where it would make sense. We would need a
clear distinction between Equation and Equality as well as methods to
convert between them.

1. Equality is a Boolean
2. Equation is Basic
3. Neither is Expr
4. You can use `.as_Boolean/.as_Equation` or something to convert between
5. Equation should be used instead of Equality for e.g. passing
equations to solve/dsolve and for returning solutions.
6. Equation should be invalid in any Boolean context
7. Equality should not support any of the arithmetic manipulations
that Equation does
8. Equation should be usable in subs or subseq or something (but
Equality should not)
9. Equation should have a solve method for making something the lhs
10. Documentation should find a way of making this understandable...

Hang on, doesn't that exclude the very operations we were just discussing, such as

3+Equation(a-3,b-3)

David

Oscar Benjamin

não lida,
1 de mai. de 2020, 18:44:0901/05/2020
para sympy
That example works fine with Equation:

In [5]: a, b = symbols('a, b')

In [6]: 3+Equation(a-3,b-3)
Out[6]: a = b

It doesn't work with Eq/Equality though. E.g. in 1.6:

In [7]: 3+Eq(a-3,b-3)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-d5998316a014> in <module>
----> 1 3+Eq(a-3,b-3)

TypeError: unsupported operand type(s) for +: 'int' and 'Equality'

--
Oscar

David Bailey

não lida,
1 de mai. de 2020, 18:59:1401/05/2020
para sy...@googlegroups.com

Yes but isn't that inconsistent with your statement "Yes, if we had Equation we should disallow its use in an expression"? Surely the example we are discussing is an Equation embedded in an expression!

Sorry, I am probably missing something!

David

Oscar Benjamin

não lida,
1 de mai. de 2020, 19:07:1701/05/2020
para sympy
There is a difference between having an object as part of an
expression and having an object that supports arithmetic operations.
Objects of type Expr support arithmetic operations and by default the
result creates an expression involving the original object so e.g. a+b
becomes Add(a, b). That's what happens with Eq in sympy 1.5 because in
1.5 Eq is a subclass of Expr so Eq(a, b)+1 becomes Add(Eq(a, b), 1)
which is nonsensical. In 1.6 Eq does not subclass Expr and also Expr
is fussier about what types it can interact with through arithmetic
operations so this gives an error.

The point of Equation is that it can support some arithmetic
operations and the result will always be an Equation and never an
expression. It should never be a part of an expression though so e.g.
Add(Equation(x, y), z) is an error because Add is of type Expr and its
arguments need to be of type Expr but Equation is not of type Expr.
That does not preclude Equation(x,y)+1 giving an Equation though.

--
Oscar

David Bailey

não lida,
2 de mai. de 2020, 07:12:3302/05/2020
para sy...@googlegroups.com
On 02/05/2020 00:07, Oscar Benjamin wrote:
There is a difference between having an object as part of an
expression and having an object that supports arithmetic operations.
Objects of type Expr support arithmetic operations and by default the
result creates an expression involving the original object so e.g. a+b
becomes Add(a, b). That's what happens with Eq in sympy 1.5 because in
1.5 Eq is a subclass of Expr so Eq(a, b)+1 becomes Add(Eq(a, b), 1)
which is nonsensical. In 1.6 Eq does not subclass Expr and also Expr
is fussier about what types it can interact with through arithmetic
operations so this gives an error.

The point of Equation is that it can support some arithmetic
operations and the result will always be an Equation and never an
expression. It should never be a part of an expression though so e.g.
Add(Equation(x, y), z) is an error because Add is of type Expr and its
arguments need to be of type Expr but Equation is not of type Expr.
That does not preclude Equation(x,y)+1 giving an Equation though.

Doesn't that feel a bit arbitrary, I mean you seem to be saying saying that

Equation(a+k,b+k)-k

where a,b, and k are SymPy symbols

would fail?

If that is the case, the new ability to add/subtract things from Equations doesn't seem that powerful.

David


Oscar Benjamin

não lida,
2 de mai. de 2020, 09:20:0502/05/2020
para sympy
I guess I haven't explained this very well. To be clear your example
sould work fine with Equation:

In [2]: a, b, k = symbols('a, b, k')

In [3]: Equation(a+k,b+k)-k
Out[3]: a = b

There needs to be a distinction between a python expression like a+b
and sympy expression like Add(a, b). I'll use lower-case expression
for a python expression and Expr for a sympy expression,

A python expression need not involve sympy types and can do all sorts
of different things e.g.:

In [9]: a = 'hel'

In [10]: b = 'lo'

In [11]: a+b
Out[11]: 'hello'

However if a and b are sympy Exprs than a python expression involving
them will create another sympy Expr so e.g. a+b becomes Add(a, b):

In [12]: a, b = symbols('a, b')

In [13]: c = a+b

In [14]: c
Out[14]: a + b

In [15]: type(c)
Out[15]: sympy.core.add.Add

I propose to have a new Equation type which is not Expr since it does
not represent a mathematical expression. It could however be used in
*python* expressions involving either Equation or Expr with the result
always being an Equation:

In [19]: eq = Equation(a, b)

In [20]: eq
Out[20]: a = b

In [21]: eq + 1
Out[21]: a + 1 = b + 1

In [22]: type(eq + 1)
Out[22]: __main__.Equation

The idea then is that we create an Equation with two Expr:

Equation(Expr, Expr) -> Equation

Then the following arithmetic operations with + would work for Equation/Expr:

Equation + Equation -> Equation
Equation + Expr -> Equation
Expr + Equation -> Equation
Expr + Expr -> Expr

Although the arithmetic operations would work the equation can not be
part of a *sympy* Expr so e.g. Add(eq, 1) should be an error if eq is
an Equation (that should only be valid where eq is an Expr).

Other useful functions to go with Equation would be a solve method e.g.:

In [23]: eq = Equation(2*x, y)

In [24]: eq
Out[24]: 2*x = y

In [25]: eq.solve(x)
Out[24]: x = y/2

This solve method would be for simple manipulations and would raise an
exception if there is not a unique solution (I guess it would have a
force option).

There should be a way to use Equation with a subs-like function e.g.:

In [26]: expr = x+y**2

In [27]: expr
Out[27]:
2
x + y

In [28]: subseq(expr, Equation(x, 2))
2
2 + y

Then functions like dsolve that currently return Equality could return
Equation and those could be used directly for substitutions.


--
Oscar

David Bailey

não lida,
2 de mai. de 2020, 13:18:3102/05/2020
para sy...@googlegroups.com

Thanks for that great explanation.  I think my confusion was that I assumed that if you typed in the expression

Equation(a+k,b+k)+k

Then the first thing that would happen would be to transform the expression to Add(Equation(add(a,k),k) and then try to evaluate the result. If that had been correct than the outer Add() expression could not be created and would generate an error.

David

Responder a todos
Responder ao autor
Encaminhar
0 nova mensagem