I have used a symbolic manipulator that has this feature but it is a
small closed source project and I don't know how they did it. It is
very valuable though -- the difference in the file size for the right
hand sides of the differential equations for the system we are
studying is several orders of magnitude -- this makes integration
times *much* faster when this common subexpression replacement method
is used.
Any ideas or references on this subject?
Thanks,
~Luke
Thanks for your interest. I am not sure if I understand right, but would
something like this solve the problem?
In [1]: var("theta")
Out[1]: θ
In [2]: e = k/sqrt(sin(theta) * cos(theta))
In [3]: e
Out[3]:
k
─────────────────────
⎽⎽⎽⎽⎽⎽⎽⎽ ⎽⎽⎽⎽⎽⎽⎽⎽
╲╱ cos(θ) *╲╱ sin(θ)
In [4]: e.subs({cos(theta): y, sin(theta): z})
Out[4]:
k
───────────
⎽⎽⎽ ⎽⎽⎽
╲╱ y *╲╱ z
Or are you looking for an algorithm to automatically collect common
subexpressions? For that, at least for me it'd help if you could be
more specific, i.e. give us some particular examples of functionality
that you'd like sympy to do but it can't. We'll then think how to
implement that.
Thanks,
Ondrej
On Sun, Jun 15, 2008 at 9:00 PM, Luke <hazel...@gmail.com> wrote:
>
> Ondrej,
> I know that Sympy has the capability to do symbolic replacement like
> you described. I guess what I'm looking for is more an algorithm to
> help identify and automatically collect common subexpressions so that
> repeated quantities are only calculated once. For each repeated
> subexpression in an equation, an intermediate variable would be
> introduced and assigned the value of the subexpression, and then all
> occurrences of the repeated subexpression in the equation would be
> replaced with the intermediate variable. By generating symbolic
Does it also go in several layers, e.g
sin(x) -> A
sqrt(A**2) -> B
etc.?
> equations this way, when it comes time to actually use them with
> numerical values (numerical integration of equations of motion, for
> example), one avoids the repetitive computation of the same
> quantity.
>
> In regards to providing an example -- writing an algorithm to automate
> the example you did by hand would be exactly what I am looking to do.
How should sympy know that you want to substitute for sin(x) and not
sqrt(sin(x)) ?
> The equations I am dealing with can be several thousand lines long at
> times, so things get extremely messy. However, within these
> equations, many of the same quantities are used repeatedly, just like
> in your example -- sin(theta) and cos(theta).
I don't understand which criteria you use to substitute the
subexpressions --- the most frequent subexpression?
>
> There is a program I use called Autolev, written by some people from
> Stanford, and it does this introduction of intermediate variables very
> effectively. Unfortunately, Autolev isn't open source. I am very
> interested in writing an open source variant of Autolev, but this part
> of the program is something that I know little about and am not sure
> yet how to approach.
Could you post here the input and output of autolev so that we can see
some examples? (if it is legal of course)
If you cannot, at least if you could describe your specific problem
you want to solve, i.e. the exact form
of the equations etc.
>
> Thanks for the response and if you have any other ideas or references,
> I'd love to hear them! I'm guessing I'm going to need to study up on
> searching, sorting, and regular expressions.
I am interested in what you want to do, especially if you want to
solve something in physics.
Your usage could improve sympy tree traversal
functionality/subsitution. SymPy's expression is basically a tree,
each element has .args property, that is basically a list. See
Basic.atoms()'s implementation, as you can see, it's just 9 lines of
code. So you'd just traverse it and compare/substitute stuff. Let us
know how the current interface feel and if you find some ways to
improve it or make it more intuitive, please write us too.
Ondrej
Actually, that example is not relevant since there is no repeated
subexpression. The idea is to reduce the amount of unnecessarily
repeated computation (usually numerical) when there are subexpressions
that pop up several times in the complete expression. For example,
Mathematica has an example of its common subexpression elimination:
http://www.wolfram.com/technology/guide/subexpressiondetection.html
--
Robert Kern
"I have come to believe that the whole world is an enigma, a harmless
enigma that is made terrible by our own mad attempt to interpret it as
though it had an underlying truth."
-- Umberto Eco
Here's some help I got a long time ago when I asked about how to do
this with Python ASTs. I never followed up on it since I had no way
(at that time) to regenerate Python code from the modified ASTs.
Ah, I got it now, thanks!
Right, that'd be a very useful feature. i.e. when one has:
1/(sqrt(sin(x)+1)*sqrt(sin(x)+2))
then one can spare some CPU by first substituting for sin(x),
evaluating and then substituting back.
So the problem could be reformulated: evaluate the symbolic expression
(numerically) using the least amount of (expensive) evaluations.
Ok, that makes sense and it's very interesting.
> On Sun, Jun 15, 2008 at 16:11, Ondrej Certik <ond...@certik.cz> wrote:
>> I don't understand which criteria you use to substitute the
>> subexpressions --- the most frequent subexpression?
>
> Here's some help I got a long time ago when I asked about how to do
> this with Python ASTs. I never followed up on it since I had no way
> (at that time) to regenerate Python code from the modified ASTs.
>
> http://groups.google.com/group/comp.lang.python/browse_thread/thread/de32cd3d1b5c3359/0c93c996bd1ad1e1
I see, you wanted to have sympy in 2003. :) I read the thread and
indeed many people were interested in it
and some even started to code something. Too bad noone has followed up
and created sympy back then ---
we would be much more advanced now.
Anyway, I created an issue for this:
http://code.google.com/p/sympy/issues/detail?id=891
Fredrik, you were investigating some approaches to evaluate
expressions quickly, do you have any thoughts on this?
Ondrej
>> Actually, that example is not relevant since there is no repeated
>> subexpression. The idea is to reduce the amount of unnecessarily
>> repeated computation (usually numerical) when there are subexpressions
>> that pop up several times in the complete expression. For example,
>> Mathematica has an example of its common subexpression elimination:
>>
>> http://www.wolfram.com/technology/guide/subexpressiondetection.html
>
> Ah, I got it now, thanks!
>
> Right, that'd be a very useful feature. i.e. when one has:
>
> 1/(sqrt(sin(x)+1)*sqrt(sin(x)+2))
>
> then one can spare some CPU by first substituting for sin(x),
> evaluating and then substituting back.
> So the problem could be reformulated: evaluate the symbolic expression
> (numerically) using the least amount of (expensive) evaluations.
I probably wouldn't go as far as that. If you are evaluating the
expression over numpy arrays, it would also be good to trade off flops
with the memory taken up by temporaries. Also, for the problem I had
in 2003, I really wanted to generate code with the common
subexpressions broken up primarily for readability rather than flops.
Here's a pseudocode sketch of an algorithm that might work:
seen_subexp = set()
to_eliminate = []
for subtree in postorder_traversal(expr):
if subtree in seen_subexp and subtree not in to_eliminate:
to_eliminate.append(subtree)
seen_subexp.add(subtree)
replacements = []
for i, subtree in enumerate(to_eliminate):
name = 'x%s' % i
sym = Symbol(name)
replacements.append((sym, subtree))
expr = expr.subs(subtree, sym)
# WARNING: modifying iterated list in-place! I think it's fine,
# but there might be clearer alternatives.
for j in range(i+1, len(to_eliminate)):
to_eliminate[j] = to_eliminate.subs(subtree, sym)
The list of replacements (in order!) and the final expression
constitute the expressions that need to be calculated. This is, of
course, untested.
Only just an idea: Could you use the numerical resuluts in the cache
for that? So you didnt need to extract all the common subexpressions.
By,
Friedrich
I've attached some nominally working code to the issue. Playing with
it, I see some suboptimal results. First, it's quadratic, but I'm not
clever enough to think of a way around that. Second, sympy's default
representation of expressions isn't well-suited for this algorithm (or
the other way around). For example:
In [1]: from cse import cse
In [2]: cse((x-y)*(z-y) + sqrt((x-y)*(z-y)))
Out[2]:
⎛ ⎽⎽⎽⎽ ⎽⎽⎽⎽⎞
⎝[(x₀, -y), (x₁, x + x₀), (x₂, x₀ + z)], x₁*x₂ + ╲╱ x₁ *╲╱ x₂ ⎠
We see that -y gets picked up as term that should be pulled out
although it only shows up in the context of (x-y). For the typical use
case, subtraction is an atomic operation, and the representation of
(x-y) as Add(Mul(NegativeOne(-1), Symbol('y')), Symbol('x')) gets in
the way.
Although the input expression has both x1 and x2 under the same
sqrt(), it gets broken out before the cse() function gets to look at
it. It would be nice to stuff everything in that term under the same
sqrt() before cse() operates. This kind of thing pops up from time to
time in my experience.
So I think there needs to be a bit of a preprocessing step to optimize
the expression specifically for cse() before it does its magic.
Generalizing this to a list of expressions (e.g. my old use case) is
an exercise left to the reader.
Look at the code I provided attached to the issue.
http://sympy.googlecode.com/issues/attachment?aid=3304519749492286715&name=cse.py
I did:
http://code.google.com/p/sympy/issues/detail?id=891#c3
> should work great. I really appreciate your comments and your help!
Thanks Robert and Fredrik for the code!
Robert's main loop is this:
for subtree in postorder_traversal(expr):
if subtree.args == ():
# Exclude atoms, since there is no point in renaming them.
continue
if subtree.args != () and subtree in seen_subexp and subtree
not in to_eliminate:
to_eliminate.append(subtree)
seen_subexp.add(subtree)
It's awesome. Then one just takes the to_eliminate list and substitute
it for x_0, x_1, ...
On Mon, Jun 16, 2008 at 8:28 AM, Robert Kern <rober...@gmail.com> wrote:
> On Sun, Jun 15, 2008 at 17:46, Ondrej Certik <ond...@certik.cz> wrote:
>> Anyway, I created an issue for this:
>>
>> http://code.google.com/p/sympy/issues/detail?id=891
>
> I've attached some nominally working code to the issue. Playing with
> it, I see some suboptimal results. First, it's quadratic, but I'm not
> clever enough to think of a way around that. Second, sympy's default
> representation of expressions isn't well-suited for this algorithm (or
> the other way around). For example:
>
> In [1]: from cse import cse
>
> In [2]: cse((x-y)*(z-y) + sqrt((x-y)*(z-y)))
> Out[2]:
> ⎛ ⎽⎽⎽⎽ ⎽⎽⎽⎽⎞
> ⎝[(x₀, -y), (x₁, x + x₀), (x₂, x₀ + z)], x₁*x₂ + ╲╱ x₁ *╲╱ x₂ ⎠
>
>
> We see that -y gets picked up as term that should be pulled out
> although it only shows up in the context of (x-y). For the typical use
> case, subtraction is an atomic operation, and the representation of
> (x-y) as Add(Mul(NegativeOne(-1), Symbol('y')), Symbol('x')) gets in
> the way.
>
> Although the input expression has both x1 and x2 under the same
> sqrt(), it gets broken out before the cse() function gets to look at
> it. It would be nice to stuff everything in that term under the same
> sqrt() before cse() operates.
Well, x-y is represented as Add(x, Mul(-1, y)) as you said. So -y is
Mul(-1,y), thus it is a common subexpression and thus it get
substituted,
I think everything is a-ok, isn't it?
So if you want to improve the algorithm, apply this trivial patch:
diff --git a/cse.py b/cse.py
--- a/cse.py
+++ b/cse.py
@@ -48,6 +48,9 @@ def cse(expr, prefix='x'):
if subtree.args == ():
# Exclude atoms, since there is no point in renaming them.
continue
+ if subtree.is_Mul and subtree.args[0].is_number:
+ # Exclude stuff like (-y)
+ continue
if subtree.args != () and subtree in seen_subexp and subtree
not in to_eliminate:
to_eliminate.append(subtree)
seen_subexp.add(subtree)
Now it works:
In [2]: cse((x-y)*(z-y) + sqrt((x-y)*(z-y)))
Out[2]:
⎛ ⎽⎽⎽⎽ ⎽⎽⎽⎽⎞
⎝[(x₀, x - y), (x₁, z - y)], x₀*x₁ + ╲╱ x₀ *╲╱ x₁ ⎠
> This kind of thing pops up from time to time in my experience.
It does, but it can be easily fixed, see above. I.e. fixing your
algorithm. :) The other option, as you said, is fixing sympy. We would
have to introduce a Sub class.
But I think it would just make things more complex. No?
>
> So I think there needs to be a bit of a preprocessing step to optimize
> the expression specifically for cse() before it does its magic.
>
> Generalizing this to a list of expressions (e.g. my old use case) is
> an exercise left to the reader.
Yep. Thanks again for the code, it will be included soon in sympy.
If you could Luke test it and maybe even prepare a patch to SymPy
together with a test, it'd be very much appreciated. :)
Ondrej
It is certainly correct for that representation. However, my claim is
that this representation is suboptimal for the purposes to which one
wants to apply common subexpression elimination. All of the use cases
hold as important one of these two criteria:
1. flop count
2. readability
CSE on the default sympy representation pessimizes both of these. For
floating point evaluation, - is an atomic operation. You would not
generate code that breaks that up into a Mul and an Add.
x0 = -1*y
x1 = x + x0
versus
x1 = x - y
Readability is much the same.
> So if you want to improve the algorithm, apply this trivial patch:
>
> diff --git a/cse.py b/cse.py
> --- a/cse.py
> +++ b/cse.py
> @@ -48,6 +48,9 @@ def cse(expr, prefix='x'):
> if subtree.args == ():
> # Exclude atoms, since there is no point in renaming them.
> continue
> + if subtree.is_Mul and subtree.args[0].is_number:
> + # Exclude stuff like (-y)
> + continue
> if subtree.args != () and subtree in seen_subexp and subtree
> not in to_eliminate:
> to_eliminate.append(subtree)
> seen_subexp.add(subtree)
>
>
> Now it works:
>
> In [2]: cse((x-y)*(z-y) + sqrt((x-y)*(z-y)))
> Out[2]:
> ⎛ ⎽⎽⎽⎽ ⎽⎽⎽⎽⎞
> ⎝[(x₀, x - y), (x₁, z - y)], x₀*x₁ + ╲╱ x₀ *╲╱ x₁ ⎠
>
>
>> This kind of thing pops up from time to time in my experience.
>
> It does, but it can be easily fixed, see above. I.e. fixing your
> algorithm. :) The other option, as you said, is fixing sympy. We would
> have to introduce a Sub class.
> But I think it would just make things more complex. No?
For all of sympy, yes. However, I think we could keep these
transformations local to the cse module. You would have to implement
just enough of Sub to replace Add(x, Mul(-constant, y)) and go back
again.
Similarly, breaking up a power of a product into a product of powers
in the default sympy representation hides optimization opportunities.
CSE could work better with a different representation.
>> So I think there needs to be a bit of a preprocessing step to optimize
>> the expression specifically for cse() before it does its magic.
I think this is the correct approach rather than putting special cases
inside cse() itself. Preprocessing to a more tractable representation,
CSE, then postprocessing allows us to add optimizations incrementally
while keeping the core algorithm clean and testable. sympy in general
doesn't need to change outside of the cse module.
Yes, I completely agree with this.
>> It does, but it can be easily fixed, see above. I.e. fixing your
>> algorithm. :) The other option, as you said, is fixing sympy. We would
>> have to introduce a Sub class.
>> But I think it would just make things more complex. No?
>
> For all of sympy, yes. However, I think we could keep these
> transformations local to the cse module. You would have to implement
> just enough of Sub to replace Add(x, Mul(-constant, y)) and go back
> again.
That's basically what my patch does. In terms of maintainability, I
think it's easier to maintain the two added lines, rather than a full
Sub class.
(Unless it doesn't cover all the corner cases, in which case we might
refactor it into a regular Sub class).
>
> Similarly, breaking up a power of a product into a product of powers
> in the default sympy representation hides optimization opportunities.
> CSE could work better with a different representation.
This also pops up from time to time. The current implementation does
what you'd do by hand in 90% of the cases, i.e. it does the right
thing.
I think we can patch Pow to do something like this:
e = Pow(a*b, 2, expand=False)
f = Pow(a*b, 2)
then
e = (a*b)**2
f = a**2 * b**2
and
e != f
We can also add methods like expand(), collect() etc. I think it
wouldn't break anything and it can be useful from time to time.
>
>>> So I think there needs to be a bit of a preprocessing step to optimize
>>> the expression specifically for cse() before it does its magic.
>
> I think this is the correct approach rather than putting special cases
> inside cse() itself. Preprocessing to a more tractable representation,
> CSE, then postprocessing allows us to add optimizations incrementally
> while keeping the core algorithm clean and testable. sympy in general
> doesn't need to change outside of the cse module.
Right. As I said above, if the only special case are those 2 lines in
my patch, then I think that's the best way. If, however, we realize we
need to add more special cases, then I'd suggest to implement the
other representation, for exactly the reasons you said.
Or to put it another way, if someone sends a patch with the things you
describe, it will surely go in (if it's robust + maintainable),
otherwise I suggest to push in your code + my 2 lines patch.
Ondrej
>>> It does, but it can be easily fixed, see above. I.e. fixing your
>>> algorithm. :) The other option, as you said, is fixing sympy. We would
>>> have to introduce a Sub class.
>>> But I think it would just make things more complex. No?
>>
>> For all of sympy, yes. However, I think we could keep these
>> transformations local to the cse module. You would have to implement
>> just enough of Sub to replace Add(x, Mul(-constant, y)) and go back
>> again.
>
> That's basically what my patch does.
Yes, but down inside the guts of the CSE algorithm. My preference is
to keep the core algorithm generic. It is currently entirely agnostic
to the actual contents of the tree.
> In terms of maintainability, I
> think it's easier to maintain the two added lines, rather than a full
> Sub class.
If that were the only optimization, I would agree. However, this
expression was the first I tested the algorithm on, and I found two
places for optimization. I think we'll find more. Preprocess, CSE,
then postprocess is more extensible, IMO, than adding special cases
inside the CSE. I think that the product-of-powers case can only
really be done by preprocessing, so we might as well do all of the
optimizations by preprocessing. Unit testing is easier by separating
the concerns of optimizing the representation and doing CSE. It opens
the possibility for the user to select the optimizations they need.
These are also things that can be added later, so having the core CSE
implementation could go in right away (pending unit tests and multiple
expressions).
Thanks for the tip. BTW, you don't seem to have a class Sub in
sympycore, definitely not exported by default.
Ondrej
Thanks for the example. Isn't just implementing a class Sub in SymPy
(+conversion methods) a better solution? It is like having the
Integral or Derivative class,
it also isn't used by default, but if the user requests it, he can have it.
I agree. But you know what Linus says[1], right :), so the current options are:
1) commit your patch as is and wait until someone fixes the x-y
problem in a general way
2) commit your patch + my patch + a good comment, that adding further
special cases are not acceptable and one should refactor it, and give
a reference to this thread,
3) do not commit anything, and wait until someone implements the general way
Each option has advantages and disadvantages, I think I myself am for
2), but if majority of you think we should just go the 1) or 3) way,
it's ok with me too.
Ondrej
> 1) commit your patch as is and wait until someone fixes the x-y
> problem in a general way
> 2) commit your patch + my patch + a good comment, that adding further
> special cases are not acceptable and one should refactor it, and give
> a reference to this thread,
> 3) do not commit anything, and wait until someone implements the general way
>
> Each option has advantages and disadvantages, I think I myself am for
> 2), but if majority of you think we should just go the 1) or 3) way,
> it's ok with me too.
I can probably implement multiple expression support + unit tests +
subtraction preprocessing in a few days time.
Excellent. In this case, let's go the 3) way.
Thanks!
Ondrej
Fredrik
Where should the code go in the package?
Let's put it to sympy/simplify/
It is in fact a simplification.
Ondrej
Any progress on this? I think it's time to make anothe release, let's
say on Monday. If you are busy, I propose to put the code that we have
in (with my patch and a comment how to proceed with fixing it
generally) and when you or anyone else finds time, it can be improved.
Ondrej
It's just about ready.
'tis done.
http://www.enthought.com/~rkern/cgi-bin/hgwebdir.cgi/sympy/
Wow, beautiful patches, thanks a lot!
I'll run couple tests and push it in.
While I am at the doc writing, I'll also write some intro about this
to our docs.
Ondrej
You should also decide where the main cse() function ought to be
exposed. I punted, and didn't expose it in sympy.* or
sympy.simplify.*.
I'd add the following patch:
# HG changeset patch
# User Ondrej Certik <ond...@certik.cz>
# Date 1215346162 -7200
# Node ID a899701f1e4f303dcc41fd4a1e00795936a035c5
# Parent d799b0e232f2d15f7c771aec318d30840506a681
Make cse accept a single expression as well.
diff --git a/sympy/simplify/cse.py b/sympy/simplify/cse.py
--- a/sympy/simplify/cse.py
+++ b/sympy/simplify/cse.py
@@ -1,7 +1,7 @@
""" Tools for doing common subexpression elimination.
"""
-from sympy import Symbol
+from sympy import Symbol, Basic
from sympy.utilities.iterables import postorder_traversal
import cse_opts
@@ -91,7 +91,7 @@ def cse(exprs, symbols=None, optimizatio
Parameters
----------
- exprs : list of sympy expressions
+ exprs : list of sympy expressions, or a single sympy expression
The expressions to reduce.
symbols : infinite iterator yielding unique Symbols
The symbols used to label the common subexpressions which are pulled
@@ -124,6 +124,9 @@ def cse(exprs, symbols=None, optimizatio
# manipulations of the module-level list in some other thread.
optimizations = list(cse_optimizations)
+ # Handle the case if just one expression was passed.
+ if isinstance(exprs, Basic):
+ exprs = [exprs]
# Preprocess the expressions to give us better optimization opportunities.
exprs = [preprocess_for_cse(e, optimizations) for e in exprs]
diff --git a/sympy/simplify/tests/test_cse.py b/sympy/simplify/tests/test_cse.py
--- a/sympy/simplify/tests/test_cse.py
+++ b/sympy/simplify/tests/test_cse.py
@@ -45,6 +45,13 @@ def test_cse_single():
assert substs == [(x0, x+y)]
assert reduced == [sqrt(x0) + x0**2]
+def test_cse_single2():
+ # Simple substitution, test for being able to pass the expression directly
+ e = Add(Pow(x+y,2), sqrt(x+y))
+ substs, reduced = cse.cse(e, optimizations=[])
+ assert substs == [(x0, x+y)]
+ assert reduced == [sqrt(x0) + x0**2]
+
def test_cse_not_possible():
# No substitution possible.
e = Add(x,y)
and export it using this patch:
# HG changeset patch
# User Ondrej Certik <ond...@certik.cz>
# Date 1215346276 -7200
# Node ID 049e4c8c531bacf82216959d2c6bfa004a662961
# Parent a899701f1e4f303dcc41fd4a1e00795936a035c5
Export cse() by default.
diff --git a/sympy/simplify/__init__.py b/sympy/simplify/__init__.py
--- a/sympy/simplify/__init__.py
+++ b/sympy/simplify/__init__.py
@@ -10,3 +10,5 @@ from rewrite import cancel, trim, apart
from rewrite import cancel, trim, apart
from sqrtdenest import sqrtdenest
+
+from cse import cse
Let me know what you think.
Ondrej
I'd rename the module, in this case. It makes it difficult to import
the module from the sympy.simplify package. This is necessary if
someone needs to change the default optimization list at runtime.
That's right. How about this patch:
# HG changeset patch
# User Ondrej Certik <ond...@certik.cz>
# Date 1215346276 -7200
# Node ID 841260b1596e0adcc1aef4695f78c02fb14c870b
# Parent a899701f1e4f303dcc41fd4a1e00795936a035c5
cse module renamed to cse_main and cse_main.cse() exported by default.
diff --git a/sympy/simplify/__init__.py b/sympy/simplify/__init__.py
--- a/sympy/simplify/__init__.py
+++ b/sympy/simplify/__init__.py
@@ -10,3 +10,5 @@ from rewrite import cancel, trim, apart
from rewrite import cancel, trim, apart
from sqrtdenest import sqrtdenest
+
+from cse_main import cse
diff --git a/sympy/simplify/cse.py b/sympy/simplify/cse_main.py
rename from sympy/simplify/cse.py
rename to sympy/simplify/cse_main.py
diff --git a/sympy/simplify/tests/test_cse.py b/sympy/simplify/tests/test_cse.py
--- a/sympy/simplify/tests/test_cse.py
+++ b/sympy/simplify/tests/test_cse.py
@@ -1,19 +1,19 @@ import itertools
import itertools
-from sympy import Add, Mul, Pow, Symbol, sin, sqrt, symbols, sympify
-from sympy.simplify import cse, cse_opts
+from sympy import Add, Mul, Pow, Symbol, sin, sqrt, symbols, sympify, cse
+from sympy.simplify import cse_main, cse_opts
w,x,y,z = symbols('wxyz')
-x0,x1,x2 = list(itertools.islice(cse.numbered_symbols(), 0, 3))
+x0,x1,x2 = list(itertools.islice(cse_main.numbered_symbols(), 0, 3))
negone = sympify(-1)
def test_numbered_symbols():
- ns = cse.numbered_symbols(prefix='y')
+ ns = cse_main.numbered_symbols(prefix='y')
assert list(itertools.islice(ns, 0, 10)) == [Symbol('y%s'%i) for
i in range(0, 10)]
- ns = cse.numbered_symbols(prefix='y')
+ ns = cse_main.numbered_symbols(prefix='y')
assert list(itertools.islice(ns, 10, 20)) == [Symbol('y%s'%i) for
i in range(10, 20)]
- ns = cse.numbered_symbols()
+ ns = cse_main.numbered_symbols()
assert list(itertools.islice(ns, 0, 10)) == [Symbol('x%s'%i) for
i in range(0, 10)]
# Dummy "optimization" functions for testing.
@@ -24,59 +24,59 @@ def opt2(expr):
return expr*z
def test_preprocess_for_cse():
- assert cse.preprocess_for_cse(x, [(opt1, None)]) == x+y
- assert cse.preprocess_for_cse(x, [(None, opt1)]) == x
- assert cse.preprocess_for_cse(x, [(None, None)]) == x
- assert cse.preprocess_for_cse(x, [(opt1, opt2)]) == x+y
- assert cse.preprocess_for_cse(x, [(opt1, None), (opt2, None)]) == (x+y)*z
+ assert cse_main.preprocess_for_cse(x, [(opt1, None)]) == x+y
+ assert cse_main.preprocess_for_cse(x, [(None, opt1)]) == x
+ assert cse_main.preprocess_for_cse(x, [(None, None)]) == x
+ assert cse_main.preprocess_for_cse(x, [(opt1, opt2)]) == x+y
+ assert cse_main.preprocess_for_cse(x, [(opt1, None), (opt2,
None)]) == (x+y)*z
def test_postprocess_for_cse():
- assert cse.postprocess_for_cse(x, [(opt1, None)]) == x
- assert cse.postprocess_for_cse(x, [(None, opt1)]) == x+y
- assert cse.postprocess_for_cse(x, [(None, None)]) == x
- assert cse.postprocess_for_cse(x, [(opt1, opt2)]) == x*z
+ assert cse_main.postprocess_for_cse(x, [(opt1, None)]) == x
+ assert cse_main.postprocess_for_cse(x, [(None, opt1)]) == x+y
+ assert cse_main.postprocess_for_cse(x, [(None, None)]) == x
+ assert cse_main.postprocess_for_cse(x, [(opt1, opt2)]) == x*z
# Note the reverse order of application.
- assert cse.postprocess_for_cse(x, [(None, opt1), (None, opt2)]) == x*z+y
+ assert cse_main.postprocess_for_cse(x, [(None, opt1), (None,
opt2)]) == x*z+y
def test_cse_single():
# Simple substitution.
e = Add(Pow(x+y,2), sqrt(x+y))
- substs, reduced = cse.cse([e], optimizations=[])
+ substs, reduced = cse([e], optimizations=[])
assert substs == [(x0, x+y)]
assert reduced == [sqrt(x0) + x0**2]
def test_cse_single2():
# Simple substitution, test for being able to pass the expression directly
e = Add(Pow(x+y,2), sqrt(x+y))
- substs, reduced = cse.cse(e, optimizations=[])
+ substs, reduced = cse(e, optimizations=[])
assert substs == [(x0, x+y)]
assert reduced == [sqrt(x0) + x0**2]
def test_cse_not_possible():
# No substitution possible.
e = Add(x,y)
- substs, reduced = cse.cse([e], optimizations=[])
+ substs, reduced = cse([e], optimizations=[])
assert substs == []
assert reduced == [x+y]
def test_nested_substitution():
# Substitution within a substitution.
e = Add(Pow(w*x+y,2), sqrt(w*x+y))
- substs, reduced = cse.cse([e], optimizations=[])
+ substs, reduced = cse([e], optimizations=[])
assert substs == [(x0, w*x), (x1, x0+y)]
assert reduced == [sqrt(x1) + x1**2]
def test_subtraction_opt():
# Make sure subtraction is optimized.
e = (x-y)*(z-y) + sin((x-y)*(z-y))
- substs, reduced = cse.cse([e],
optimizations=[(cse_opts.sub_pre,cse_opts.sub_post)])
+ substs, reduced = cse([e],
optimizations=[(cse_opts.sub_pre,cse_opts.sub_post)])
assert substs == [(x0, z-y), (x1, x-y), (x2, x0*x1)]
assert reduced == [x2 + sin(x2)]
def test_multiple_expressions():
e1 = (x+y)*z
e2 = (x+y)*w
- substs, reduced = cse.cse([e1, e2], optimizations=[])
+ substs, reduced = cse([e1, e2], optimizations=[])
assert substs == [(x0, x+y)]
assert reduced == [x0*z, x0*w]
Ondrej
Works for me.
Great. It's in.
Ondrej
The docs are here:
http://docs.sympy.org/modules/rewriting.html#module-sympy.simplify.cse_main
Stefan, what is the preferred way to document things in numpy? I mean
what kind of REST markup one should use in docstrings, so that it
looks nice in sphinx.
Looking here:
http://mentat.za.net/numpy/refguide/functions.xhtml
it seems like you are using stuff like
"
Paremeters
--------------
"
Also is this now the official numpy docs style guide:
http://projects.scipy.org/scipy/numpy/browser/trunk/numpy/doc/example.py#L37
?
in the docstrings, so in this light, this patch:
http://hg.sympy.org/sympy/rev/2a85bc165b30
is not correct. But without it, the sphinx refuses to generate the
docs. How did you overcome this problem?
Ondrej
The NumPy docstrings are not Sphinx-compatible reST, mainly because we
use top-level constructs (such as Parameters) in nested environments.
We take all the docstrings and parse it through a translator before we
send it to Sphinx:
https://code.launchpad.net/~stefanv/scipy/numpy-refguide
The reference guide is not yet looking very professional, but I hope
to invest some time in its presentation this week.
Matplotlib followed a bit of a different tack, and used Sphinx
compatible markup, which is extracted using the `autodoc` feature.
They also have plotting and LaTeX directives (LaTeX being rendered by
their own mathtext):
http://matplotlib.sourceforge.net/doc/html/users/mathtext.html
Regards
Stéfan