-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
solvers - added Min/Max support for decompogen #23021
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
✅ Hi, I am the SymPy bot (v162). 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:
This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.11. Click here to see the pull request description that was parsed.
Update The release notes on the wiki have been updated. |
Benchmark results from GitHub Actions Lower numbers are good, higher numbers are bad. A ratio less than 1 Significantly changed benchmark results (PR vs master) Significantly changed benchmark results (master vs previous release) before after ratio
[907895ac] [706ab904]
- 217±0.8ms 133±0.5ms 0.61 large_exprs.TimeLargeExpressionOperations.time_subs
- 220±1μs 109±0.2μs 0.50 matrices.TimeMatrixExpression.time_MatMul
- 13.9±0.02ms 7.48±0.04ms 0.54 matrices.TimeMatrixExpression.time_MatMul_doit
- 4.21±0.02s 310±2ms 0.07 polygon.PolygonArbitraryPoint.time_bench01
+ 3.27±0.02ms 5.61±0.02ms 1.71 solve.TimeMatrixOperations.time_det(4, 2)
+ 3.28±0.01ms 5.59±0.03ms 1.71 solve.TimeMatrixOperations.time_det_bareiss(4, 2)
+ 37.7±0.2ms 67.5±0.1ms 1.79 solve.TimeMatrixSolvePyDySlow.time_linsolve(1)
+ 38.0±0.1ms 67.4±0.3ms 1.77 solve.TimeMatrixSolvePyDySlow.time_solve(1)
Full benchmark results can be found as artifacts in GitHub Actions |
@smichr |
ahh -- that is similar to what you had before. But I don't know much about |
@smichr The benchmark test is failing is there any fix for it? |
I don't think it is related -- it is failing on my PR, too. |
raises(TypeError, lambda: decompogen(x < 5, x)) | ||
u = 2*x + 3 | ||
raises(TypeError, lambda: decompogen(Max(u, u**2), x)) | ||
assert decompogen(Max(u, u**2, y), x) == [Max(x, y), 2*x + 3, x**2] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be [Max(x, x**2, y), 2*x + 3]
according to docstring:
Given an expression ``f``, returns a list ``[f_1, f_2, ..., f_n]``,
where::
f = f_1 o f_2 o ... f_n = f_1(f_2(... f_n))
Note: This is a General decomposition function. It also decomposes
Polynomials. For only Polynomial decomposition see ``decompose`` in polys.
Examples
========
>>> from sympy.abc import x
>>> from sympy import decompogen, sqrt, sin, cos
>>> decompogen(sin(cos(x)), x)
[sin(x), cos(x)]
So if you keep replacing the left expression's x with the expression to the right you should get the original function.
Given [Min(x,x**2, y),2*x+3]
, start with leftmost, Min(x, x**2, y)
and replace x
with 2*x+3
to give Min(2*x + 3, (2*x + 3)**2, y)
, the original expression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic should be something like
args = list(f.args)
d0 = None
for i,a in enumerate(args):
if not a.has_symbol: continue
d = decompogen(a, symbol)
if d0 is None:
d0= d
continue
if d[1:] != d0[1:]:
raise ValueError
args[i] = d[0]
return [f.func(*args)] + d[1:]
So a test like u = 2*x + 3; decompogen(Min(u, u**2, y, u**3), x)
should give [Min(x, x**2, y, x**3), 2*x + y]
but there might be re-ordering of the args -- as long as the args are all there it should be fine.
(Really, it seems like decompogen
should use cse
so decompogen(cos(2*x+3) + sin((2*x+3)**2 + y), x)
could give cos(x) + sin(x**2 + y), 2*x + 3
. But that is a different issue.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
decompogen(Min(u, u**2, y, u**3), x)
is giving a ValueError
for this change. Also decompogen(Min(5, x**2), x)
now gives [Min(5, x**2)]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if all(a.has(symbol) for a in f.args):
raise TypeError('cannot decompose %s' % f)
args = list(f.args)
d0 = None
for i, a in enumerate(args):
if not a.has(symbol):
continue
d = decompogen(a, symbol)
if d0 is None:
d0 = d
continue
args[i] = d[0]
return [f.subs(args[1], symbol)] + d0
@smichr This change is working and is giving the correct results but I'm not sure whether it is the right way?
diff --git a/sympy/solvers/decompogen.py b/sympy/solvers/decompogen.py
index 51d380b86a..6628155129 100644
--- a/sympy/solvers/decompogen.py
+++ b/sympy/solvers/decompogen.py
@@ -40,8 +40,6 @@ def decompogen(f, symbol):
if symbol not in f.free_symbols:
return [f]
- result = []
-
# ===== Simple Functions ===== #
if isinstance(f, (Function, Pow)):
if f.is_Pow and f.base == S.Exp1:
@@ -50,21 +48,26 @@ def decompogen(f, symbol):
arg = f.args[0]
if arg == symbol:
return [f]
- result += [f.subs(arg, symbol)] + decompogen(arg, symbol)
- return result
+ return [f.subs(arg, symbol)] + decompogen(arg, symbol)
# ===== Min/Max Functions ===== #
if isinstance(f, (Min, Max)):
args = list(f.args)
- iargs = [i for i, a in enumerate(f.args) if a.has(symbol)]
- if len(iargs) == len(args):
- raise TypeError('cannot decompose %s' % f)
- dec = []
- for i in iargs:
- dec.extend(decompogen(args[i], symbol))
- args[i] = symbol
- result += [f.func(*args)] + list(uniq(dec))
- return result
+ d0 = None
+ for i, a in enumerate(args):
+ if not a.has_free(symbol):
+ continue
+ d = decompogen(a, symbol)
+ if len(d) == 1:
+ d = [symbol] + d
+ if d0 is None:
+ d0 = d[1:]
+ elif d[1:] != d0:
+ raise TypeError('cannot decompose %s' % f)
+ args[i] = d[0]
+ if d0 == [symbol]:
+ return [f]
+ return [f.func(*args)] + d0
# ===== Convert to Polynomial ===== #
fp = Poly(f)
@@ -73,13 +76,11 @@ def decompogen(f, symbol):
if len(gens) == 1 and gens[0] != symbol:
f1 = f.subs(gens[0], symbol)
f2 = gens[0]
- result += [f1] + decompogen(f2, symbol)
- return result
+ return [f1] + decompogen(f2, symbol)
# ===== Polynomial decompose() ====== #
try:
- result += decompose(f)
- return result
+ return decompose(f)
except ValueError:
return [f]
diff --git a/sympy/solvers/tests/test_decompogen.py b/sympy/solvers/tests/test_decompogen.py
index 22bcde1d44..e616b322af 100644
--- a/sympy/solvers/tests/test_decompogen.py
+++ b/sympy/solvers/tests/test_decompogen.py
@@ -19,11 +19,11 @@ def test_decompogen():
assert decompogen(Abs(cos(y)**2 + 3*cos(x) - 4), x) == [Abs(x), 3*x + cos(y)**2 - 4, cos(x)]
assert decompogen(x, y) == [x]
assert decompogen(1, x) == [1]
- assert decompogen(Max(3, x), x) == [Max(3, x), x]
+ assert decompogen(Max(3, x), x) == [Max(3, x)]
raises(TypeError, lambda: decompogen(x < 5, x))
u = 2*x + 3
- raises(TypeError, lambda: decompogen(Max(u, u**2), x))
- assert decompogen(Max(u, u**2, y), x) == [Max(x, y), 2*x + 3, x**2]
+ assert decompogen(Max(sqrt(u),(u)**2), x) == [Max(sqrt(x), x**2), u]
+ assert decompogen(Max(u, u**2, y), x) == [Max(x, x**2, y), u]
def test_decompogen_poly(): |
Co-authored-by: Christopher Smith <smichr@gmail.com>
Co-authored-by: Christopher Smith <smichr@gmail.com>
e6fbb1e
to
c5704bb
Compare
ping @smichr |
@smichr I think the latest test that you've added should be changed.
|
+1 (yes, my test should have wrapped rhs in |
References to other Issues or PRs
Brief description of what is fixed or changed
Fixes #13612
Closes #13623 and Closes #18517
Before addition:
After addition:
Other comments
Release Notes
Min
/Max
support fordecompogen
.