sympy
sympy copied to clipboard
Let Equality objects be used as substitution arguments
Brief description of what is fixed or changed
This PR lets Equality objects with a lone free Symbol on the left-hand side be used as an argument for subs().
force = Eq(F, M*A)
angular_accel = Eq(A, v**2/R)
force.subs(angular_accel) # acts like .subs(A, v**2/R)
This is very useful for substituting the answer to a recently-solve()'d equation into another one.
Release Notes
- core
- Allowed
Equalitys with a left-hand side consisting of a single free symbol to be used as an argument for thesubsfunction. [e.g.(m*a).subs(Eq(a, v**2/R))]
- Allowed
:white_check_mark:
Hi, I am the SymPy bot. I'm here to help you write a release notes entry. Please read the guide on how to write release notes.
Your release notes are in good order.
Here is what the release notes will look like:
- core
- Allowed
Equalitys with a left-hand side consisting of a single free symbol to be used as an argument for thesubsfunction. [e.g.(m*a).subs(Eq(a, v**2/R))] (#26399 by @ByThePowerOfScience and @smichr)
- Allowed
This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.13.
Click here to see the pull request description that was parsed.
#### Brief description of what is fixed or changed
This PR lets `Equality` objects with a lone free `Symbol` on the left-hand side be used as an argument for `subs()`.
```python
force = Eq(F, M*A)
angular_accel = Eq(A, v**2/R)
force.subs(angular_accel) # acts like .subs(A, v**2/R)
```
This is very useful for substituting the answer to a recently-`solve()`'d equation into another one.
#### Release Notes
<!-- Write the release notes for this release below between the BEGIN and END
statements. The basic format is a bulleted list with the name of the subpackage
and the release note for this PR. For example:
* solvers
* Added a new solver for logarithmic equations.
* functions
* Fixed a bug with log of integers. Formerly, `log(-x)` incorrectly gave `-log(x)`.
* physics.units
* Corrected a semantical error in the conversion between volt and statvolt which
reported the volt as being larger than the statvolt.
or if no release note(s) should be included use:
NO ENTRY
See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
information on how to write release notes. The bot will check your release
notes automatically to see if they are formatted correctly. -->
<!-- BEGIN RELEASE NOTES -->
* core
* Allowed `Equality`s with a left-hand side consisting of a single free symbol to be used as an argument for the `subs` function. [e.g. `(m*a).subs(Eq(a, v**2/R))`]
<!-- END RELEASE NOTES -->
Wait, can you target entire expressions for replacement in subs? If so, I should remove that check for a single variable on the lhs.
I'm not sure how needed this is since expr.subs(*eq.args) or expr.subs(*solve(linear, sym, dict=True) both work, where eq is an Equality and linear is an equation that is linear in sym.
I'm not sure how needed this is since
expr.subs(*eq.args)or expr.subs(*solve(linear, sym, dict=True)both work, whereeqis an Equality andlinearis an equation that is linear insym`.
Considering that even when looking at the source code I couldn't find the args property, it's probably better to make it as intuitive as possible. People probably shouldn't have to dig through the source code to do something that should work intuitively.
Besides, it's a nice convenience factor that doesn't add any overhead, so there's no reason not to imo.
EDIT: Actually, something that would even more useful is being able to use a set of Equalities as an argument. Also important for consistency's sake.
Wait, can you target entire expressions for replacement in
subs? If so
Yes - but it only works if the expression is an exact leaf in the expression true (usually). I don't see any harm in telling the user what the semantics are, i.e. e.subs(Eq(a,b)) is short for e.subs(a, b).
Besides, it's a nice convenience factor that doesn't add any overhead, so there's no reason not to imo.
Unfortunately, I'm generally +0 for these changes because of triviality.
I also think that there is Eq.lhs, Eq.rhs to unpack the arguments of Eq.
I understand some mathematical ideas behind how term system, substitution, unify, or something technical works. And I generally had no problem or significant inconvenience working with status quo.
Besides, it's a nice convenience factor that doesn't add any overhead, so there's no reason not to imo.
Unfortunately, I'm generally +0 for these changes because of triviality. I also think that there is
Eq.lhs, Eq.rhsto unpack the arguments ofEq. I understand some mathematical ideas behind how term system, substitution, unify, or something technical works. And I generally had no problem or significant inconvenience working with status quo.
It's just a matter of how often you do it. When working with larger sets of equations, you're frequently solving an equation in terms of a variable and immediately substituting it in for that variable. Not being able to do what's intuitive is a small thing on its own, but a large thing when you're doing it all the time.
It's mainly in physics, where you're building one massive super-equation from a hundred different relationships that inevitably converge on Newton's second law :P
The API for subs, is already messy, I appreciate the efforts to clean up to be more straightforward for users.
I'm +0, too. Solutions to equations can already be obtained as a dictionary. And we don't allow multiple dictionaries so (x+y).subs([{x:1},{y:2}]) does not work. But that sort of thing is already handled in python as
d = {x:1}; d.update({y:2}); (x+y).subs(d)->3
If someone had a bunch of equalities to apply its just expr.subs([i.args for i in equalities]).
Although I tend to agree that the overhead factor argument, something moves me toward -1/2: this opens up semantic issues like "why doesn't x.subs(Eq(y, x)) work?" We are asking an arbitrarily ordered object to act as an ordered object. And just stating that now makes me feel even more -1 for the same reasons that we have frowned upon the idea of treating Eq like an Equation. If a user wants to write a helper routine to turn Eqs into subsitution tuples, they are free to do so. But supporting that from SymPy's end does not seem like a good idea.
Although I tend to agree that the overhead factor argument, something moves me toward -1/2: this opens up semantic issues like "why doesn't
x.subs(Eq(y, x))work?"
The reason I targeted it that way is because of how it interacts with the rest of SymPy, namely the solve() function. If you solve an Equality in terms of a Symbol, it returns an Equality with that Symbol on the left. This change would allow people to chain together solve calls into substitutions.
It's also how it works in other CASs.
But you're absolutely right, it could check if there's only one side with an isolated symbol and do that substitution! Though now that I think about it, that would stop reverse-substitution condensing multiple symbols into a single one. (E.g. 1/period = freq)
If there isn't already a Relational.swap function, that would be a good thing to add in combination with this change to solve that particular issue.
But again, Equality.args just isn't mentioned in the documentation. I've been deep in the source code and this thread was still the first time I heard of the property. If that's the only intended way to do this common thing, it should be documented and explained. But then if that's the standardized way to do it, then why not make it officially standardized so they can do it intuitively? This is like a few dozen bytes for a decent amount of hassle; it's not anything that could be considered close to a loss.
If a user wants to write a helper routine to turn Eqs into subsitution tuples, they are free to do so. But supporting that from SymPy's end does not seem like a good idea.
Well that's kind of a crappy way to look at things. If quality of life features were always third-party plugins, we'd still be using community-made VBS scripts in Excel for counting cells.
The extensible nature of SymPy is an amazing feature, but it can't be relied on as the end-all-be-all of development. I've spent roughly 100 hours troubleshooting various helper functions for this library; that's a tall order to ask of a mathematician who might not even know programming. Especially for something as trivial as this that should quite frankly already be a feature.
Especially for something as trivial as this that should quite frankly already be a feature
I'll ask for feedback on the mailing list.
Relational.swap
>>> Eq(x,y).reversed
Eq(y, x)
But again, Equality.args just isn't mentioned in the documentation
One could use (eq.lhs, eq.rhs) if args is not known.
Accepting other containers (today Eq, tomorrow Matrix or TableForm) violates the principle of making a routine do one thing well and erroring towards "including the batteries." Let's see what others say on the mailing list.
This is implemented in Algebra_with_Sympy, which uses a fork of Sympy that includes an additional class called Equation. This additional class avoids some problems (e.g. automatic collapse to a boolean) that are encountered when trying to use Eq for things such as this. Maybe we should include Equation in the main sympy branch? The Equation class was originally developed as a project within Sympy but got bogged down in details about how the development team wanted it to behave. In the meantime, using Algebra_with_Sympy may meet many of @ByThePowerOfScience's needs.