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 On Thursday, February 4, 2021 at 8:35:47 PM UTC-6 gu...@uwosh.edu wrote: > 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 > <https://github.com/sympy/SymPEPs/pull/1#pullrequestreview-575020237>. > 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 > On Thursday, February 4, 2021 at 4:51:31 PM UTC-6 asme...@gmail.com wrote: > >> 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 >> >> > >> > 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 >> > >> > On Thu, Jan 21, 2021 at 4:29 AM Aaron Meurer <asme...@gmail.com> >> wrote: >> > > >> > > 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 >> > > >> > > > >> > > > How important is the convenience of being able to wrap equations in >> operations? >> > > > What operations are you aware of where this behavior could cause >> problems? >> > > > >> > > > >> > > > On Wednesday, January 20, 2021 at 2:45:44 PM UTC-6 Oscar wrote: >> > > >> >> > > >> 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 >> > > >> >> > > >> >> > > >> >> > > >> >> > > >> >> > > >> On Wed, 20 Jan 2021 at 00:58, gu...@uwosh.edu <gu...@uwosh.edu> >> wrote: >> > > >> > >> > > >> > 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 >> > > >> > >> > > >> > On Tuesday, January 19, 2021 at 5:34:47 PM UTC-6 >> asme...@gmail.com wrote: >> > > >> >> >> > > >> >> Is there a pull request with the code associated with this, or >> is it >> > > >> >> only in the Binder for now? >> > > >> >> >> > > >> > -- >> > > >> > 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. >> >> >> > > > >> > > > -- >> > > > 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/d7f67a32-381b-4ca1-baa0-da6c240c4c98n%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 sympy+un...@googlegroups.com. >> > > To view this discussion on the web visit >> https://groups.google.com/d/msgid/sympy/CAKgW%3D6KSj3NTWDgqr7WjeTFf%3DkHrAtvk7sxkW%2Bzm_RbLO72ZWg%40mail.gmail.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 sympy+un...@googlegroups.com. >> > 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. >> >> >> > -- 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+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/7d048bd4-47ad-471e-bef5-35bb42b58c24n%40googlegroups.com.