From profiling your code, it appears that nearly all the time is spent
in integrate().
This shows quite clearly that we need to optimize the integration code for
polynomials.
Fredrik
> Hi, I just implemented a small toolbox for finite element calculations
> based on Lagrangian elements
> and compared it to similar code using GiNaC. The difference in
> efficiency is remarkable. My sympy
> code takes about 20 seconds where corresponding GiNaC code is below
> one second.
Thanks a lot for writing this. I am aware of SyFi [1] and I wanted to
to write something like you just wrote in sympy. For those who want to
know more about it, I suggest to read the tutorial [2], which explains
everything. I myself am using the libmesh [3] library for finite
elements, which is very good. But the SyFi's approach is very nice.
Did you try it on some real example? How does it compare to the
traditional approach, that libmesh is using? BTW, I am glad that SyFi
is using swiginac[4], that we wrote with Ola Skavhaug. I thought that
when I wrap ginac to python, I'll get an easy to use symbolic library,
but I realized this is not the way.
Can I add your example to the examples directory in SymPy? We use a BSD license.
> Any comments on this ?
As Fredrik has said, we need to improve the integration of
polynomials. See recently discussed issues for that:
http://code.google.com/p/sympy/issues/detail?id=461
http://code.google.com/p/sympy/issues/detail?id=463
But generally, Python is like 200 slower than C++ in my experience, so
this is what has to be expected.
Could you tell us what are the future plans with SyFi and also
Fenics[5] etc? Last time I tried that, it took quite a long time to
compile and it wasn't as versatile as libmesh (particularly I need to
assign boundary conditions and changing conductivity in the body,
which wasn't implemented at that time, but libmesh can do that
easily). Generally I like the fenics ideas, but it wasn't yet ready
for a production use. So I am curious what your own plans are, as I
would like to help if I like it, because good finite elements in
Python is exactly what I need.
Ondrej
[1] http://heim.ifi.uio.no/~kent-and/software/SyFi/doc/SyFi.html
[2] http://heim.ifi.uio.no/~kent-and/software/SyFi/doc/tutorial/tutorial.pdf
[3] http://libmesh.sourceforge.net/
[4] http://swiginac.berlios.de/
[5] http://www.fenics.org/wiki/FEniCS_Project
> But generally, Python is like 200 slower than C++ in my experience, so
> this is what has to be expected.
I'd say it's closer to 20x slower for most code. (In fact, the
Computer Language Benchmarks Game lists Python as 17x slower than C++
on average.) There gap is larger for number crunching, but symbolic
computing doesn't suffer so badly since operations like comparions and
slicing can be delegated to Python built-in functions.
Did anyone compare sympycore with a C/C++ computer algebra system yet?
IIRC we compared some operation to Maxima and sympycore was 7x or so
slower.
Fredrik
Assembly is enough for me, I need to solve the eigenproblem anyway, so
I use blzpack/arpack/primme for that.
I'll try it, this sounds pretty exciting.
> Many of the Fenics projects have been in a state of flux for the last
> year. But this
> is improving. The future plan of SyFi is to come up with a nice user
> interface/language such
> that SyFi will work as a compiler translating high-level finite
> element python code
> to efficient low-level C++ code.
>
> The reason why I am looking at Sympy is that although I am fairly
> happy with GiNaC, it has its issues.
Could you be more specific what issues GiNaC has? The only problem
with ginac that I know of is that it is very difficult to create new
functions,
but as far as I know, SyFi just needs polynomials.
> Still, I think I'd prefer Sympy (except the efficiency).
>
> Can sympy generate C++ code ?
Not yet, but this is fairly easy to do. We can generate a Python code:
In [1]: e = sin(x)**4/y**(-2)
In [2]: e
Out[2]:
2 4
y *sin (x)
In [3]: Basic.set_repr_level(0)
Out[3]: 2
In [4]: e
Out[4]: Mul(Pow(Symbol('y'), Integer(2)), Pow(sin(Symbol('x')), Integer(4)))
So we can generate C++ code as well. But of course only a subset of
sympy features can be exported to C++. Mainly just an expression and a
way to substitute to that
expression. So the above should translate to something like this:
float x;
float y;
e = pow(sin(x), 4)*pow(y,2);
is that correct? If you could give me more info what exactly you need,
I'll implement that.
> > From profiling your code, it appears that nearly all the time is spent
> > in integrate().
> > This shows quite clearly that we need to optimize the integration code for
> > polynomials.
> >
> > Fredrik
>
> Ok thanks, is this easy to do ?
I think it should be fairly easy, that's why I wrote SymPy, so that
those things are easy. We just need to go to:
http://hg.sympy.org/sympy/file/c8da4f2b2324/sympy/integrals/integrals.py
line 122, and we use a general (slow) algorithm to integrate a lot of
functions. So we just check, if we got a polynomial, extract the
coefficients and integrate it. We can use functions from:
http://hg.sympy.org/sympy/file/132066377866/sympy/polynomials/base.py
There is a Polynomial class in there, so my bet is just to implement
.integrate() in that class, to be as efficient as possible and than
just fix ingetrals.py, to try to convert to polynomial if it is a
polynomial.
I'll try it in the evening, if it speeds things up.
Ondrej
So indeed I sped things up by a factor of 10x. See:
http://code.google.com/p/sympy/issues/detail?id=470
for details and a patch. Now it needs to be polished and committed.
Your example now runs for 2.3s. How fast is ginac on this exactly?
Ondrej
http://code.google.com/p/sympy/issues/detail?id=476
Thanks for noticing. This used to work, but it isn't apparently now in
SymPy. We'll fix that.
> Or when doing G(x) = N(x)*M(x), G is not the expanded product of N and
> M, but keeps
> N and M.
Is this the issue:
http://code.google.com/p/sympycore/issues/detail?id=18
?
So you want:
In [4]: f = x+y
In [5]: g = x+z
In [6]: f*g
Out[6]: (x + y)*(x + z)
In [7]: f+g
Out[7]: y + z + 2*x
So [6] is ok, but you want [7] to be "x+y+x+z" instead? Is it because
of memory requirements (see that issue for more details)?
>
> I guess SymPy and GiNaC behave similarly in these matters, but I guess
> it is easier to implement
> such features in SymPy.
In theory yes. There is the speed issue though with SymPy, it depends
if we can cope with that.
> > float x;
> > float y;
> > e = pow(sin(x), 4)*pow(y,2);
> >
> > is that correct? If you could give me more info what exactly you need,
> > I'll implement that.
> >
>
> This is what I need, although I'd like double instead of float.
I created an issue for that:
http://code.google.com/p/sympy/issues/detail?id=475
feel free to discuss it in there.
> >
> > So indeed I sped things up by a factor of 10x. See:
> >
> > http://code.google.com/p/sympy/issues/detail?id=470
>
> How do I download this ?
you follow the link in the issue to this page:
http://groups.google.com/group/sympy-patches/browse_thread/thread/c51231bb0fd91c71
and download the integration.patch. Then
$ hg clone http://hg.sympy.org/sympy/
$ cd sympy/
$ hg import ~/Desktop/Downloads/integration.patch
applying /home/ondra/Desktop/Downloads/integration.patch
$ time python examples/fem_test.py
1/60 0 -1/360 0 -1/90 -1/360
0 4/45 0 2/45 2/45 -1/90
-1/360 0 1/60 -1/90 0 -1/360
0 2/45 -1/90 4/45 2/45 0
-1/90 2/45 0 2/45 4/45 0
-1/360 -1/90 -1/360 0 0 1/60
real 0m2.486s
user 0m2.460s
sys 0m0.024s
And that's it. BTW, we are still reviewing the patch, so for example
it errorneously modifies your example by doing .expand() in there,
which is not necessary, etc.
So still 8x slower than ginac, but I consider it a very successful
result. Maybe it can still be improved, for example we are converting
to a polynomial and back, many times, this slows things down. The
problem is, I don't know a clear cut way to over come that, see:
http://code.google.com/p/sympy/issues/detail?id=477
If you (and anyone) could give us your thoughts on that issue, it'd be cool.
Ondrej
But I think both ginac and SymPy work in this way already:
In [1]: (x+1)**12 + (x-1)**12
Out[1]:
12 12
(1 + x) + (1 - x)
In [2]: f = (x+1)**12 + (x-1)**12
In [3]: f
Out[3]:
12 12
(1 + x) + (1 - x)
In [4]: f.expand()
Out[4]:
12 2 10 4 8 6
2 + 2*x + 132*x + 132*x + 990*x + 990*x + 1848*x
SymPy's automatic evaluation works in a way to only simplify things,
that can be done in a very fast way. Expanding doesn't belong to that
categhory.
> BTW: I've tested the patch, great speed-up, great work!
Thanks! It will be committed and released soon.
Ondrej