-
Notifications
You must be signed in to change notification settings - Fork 67
Heterogeneous output in Lambdify #112
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
@bjodah, Lambdify's Here's some python code that I wrote, outputs_ravel = list(itertools.chain(*outputs))
try:
cb = symengine.Lambdify(inputs, outputs_ravel, backend="llvm")
except TypeError:
cb = symengine.Lambdify(inputs, outputs_ravel)
def func(*args):
result = []
n = numpy.empty(len(outputs_ravel))
t = cb.unsafe_real(np.concatenate([arg.ravel() for arg in args]), n)
start = 0
for output in outputs:
elems = reduce(mul, output.shape)
result.append(n[start:start+elems].reshape(output.shape))
start += elems
return result
return func This makes SymEngine as fast as PyDy's cythonize. Updated the timings here, symengine/symengine#1094 (comment) |
@isuruf |
@isuruf I added the |
I'm fine with making numpy a hard dependency of just Lambdify |
@bjodah, note that in #112 (comment) I've used unsafe_real and it has memoryviews in it, so there might be another issue as well. |
I'm looking into using SymEngine in the codegen tutorial at SciPy. But when I try to compile symengine (not the python wrapper) with llvm I get a test failure:
I have created this Dockerized environment which reproduces the error: I tried to follow the Travis scripts closely but obviously I failed. Any idea what I am doing wrong? EDIT: I will use symengine from the symengine channel instead. |
I think this is because llvm was compiled with gcc 4.8.5 and you are using gcc 5.4.0. I'm surprised that there were no link errors. |
could be, modifying CPPFLAGS did not help:
gives same error. I thought setting CXX and CC was enough (I set them to use clang with ccache):
|
Travis CI tests for Python 3.2 using gcc 3.7 give an error: Cython generated C contains uninitialized references.
26ddbaf
to
dc1877e
Compare
Current status of
(speed-up factors below 1 before moving |
@bjodah, can you post the timings without |
Sure, that was a factor of ~11 (my local uncommited state using numpy.ndarray is currently seeing ~7.5 for |
Hmm... those numbers were against SymPy master. Using sympy-1.0 I now get:
so apparently SymPy master's lambdify has had a regression (1/3 of the performance). |
Comparing against sympy-1.0 & using -O1:
|
I think making NumPy a hard requirement for
on that last point: I have to implement that still. What is the best approach for that? (if we want to make NumPy i hard requirement that is). I'm thinking putting the whole Lambdify definition inside a block?, i.e.
|
(regarding the |
If you have some time, can you run this example, pydy/pydy#360 ? If not, I'll be able to run it over the weekend. |
I'm not sure if |
For
not using SymEngine (note that setting USE_SYMENGINE=0 causes sympy to use symengine...):
|
I ran the benchmark with master, PR + ManualLLVM, PR + LLVM
ManualLLVM is still faster,
|
Oops, pressed enter before finishing commenting.
Compared to master, this PR is better.
|
OK, so I extracted a small test case reproducing ManualLLVM being faster:
Will work on improving that benchmark. |
That benchmark also has one output. We should get a benchmark for heterogeneous output in Lambdify. |
you're right. I'll update the benchmark (tomorrow or Monday) to also calculate: f(t), the Jacobian matrix of f(t) and df/dt in the same call. |
1ca217c
to
3b2182f
Compare
|
symengine/lib/symengine_wrapper.pyx
Outdated
cdef int *accum_out_sizes | ||
cdef object numpy_dtype | ||
|
||
def __cinit__(self, args, *exprs, bool real=True): |
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.
Keyword-only arguments are python 3 only right?
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.
I believe keyword only args are when **kwargs
is also present.
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.
See https://www.python.org/dev/peps/pep-3102/
Syntactically, the proposed changes are fairly simple. The first change is to allow regular arguments to appear after a varargs argument:
def sortwords(*wordlist, case_sensitive=False):
...
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.
Ah, I see, but Cython supports it even for Python 2 right?
Just checked: kw-only arguments are supported even in Python 2 when using Cython.
0f333e9
to
871017c
Compare
@isuruf I think this might be ready for review |
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.
Looks good to me.
@certik, can you have a look? This PR adds a hard dependency on numpy for Lambdify functionality to keep the code simpler and a little bit faster.
symengine/__init__.py
Outdated
if have_numpy: | ||
from .lib.symengine_wrapper import Lambdify, LambdifyCSE | ||
|
||
def lambdify(args, exprs): |
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.
It's better to have the real
and backend
variables here.
@@ -59,6 +58,14 @@ else() | |||
set(HAVE_SYMENGINE_LLVM False) | |||
endif() | |||
|
|||
if(WITH_NUMPY) |
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.
WITH_NUMPY
should be a cached variable.
CMakeLists.txt
Outdated
@@ -59,6 +58,14 @@ else() | |||
set(HAVE_SYMENGINE_LLVM False) | |||
endif() | |||
|
|||
if(WITH_NUMPY) | |||
find_package(NumPy REQUIRED) | |||
include_directories(${NUMPY_INCLUDE_PATH}) |
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.
Are we not linking the numpy libraries?
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.
I just checked and there are no symbols from numpy libraries linked in. It seems we are not using anything that requires linking. I wonder whether that means we can build with one numpy version and have it work on other numpy versions.
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.
Yes, it is my understanding that the NumPy C API is largely back-compatible. And no, the numpy functions used in the code uses the python layer (e.g. numpy.empty). It is possible that we could gain some performance by directly calling the NumPy C API in __call__
(that would then require linking).
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 so, I think we can just use numpy.ndarray.ctypes
to get a array pointer and have numpy only as a runtime requirement. I'll have a look to see if this is possible.
symengine/lib/symengine_wrapper.pyx
Outdated
cdef list out_shapes | ||
cdef readonly bint real | ||
cdef readonly int n_exprs | ||
cdef int *out_sizes |
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.
Can this be a std::vector?
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.
That looks good to me. Depending on NumPy is fine I think.
If people object against that, we can try to make it optional, but I think sympy and symengine belongs to the scipy stack, and all the scipy stack depends on numpy. So I have no problems with it.
To make it clear --- |
np.arange creates a numpy array with long int elements which is 32 bit on windows.
Thanks for the PR, @bjodah |
@bjodah, packaging symengine for conda with numpy as a build-time dependency means the conda package has to be compiled for each numpy version. I'd like to avoid that if possible with numpy only as a runtime requirement. Can you try my branch at https://github.com/isuruf/symengine.py/tree/memview and see if memviews have a performance penalty? I ran the benchmarks myself and for me I don't see much difference. |
ping, @bjodah |
@isuruf just tried it. The performance is the same on my machine. +1 for using memviews |
To address #107.
There are some test failures still. I'll update and ping once this is ready for review.
I changed my mind and this now only returns a tuple if multiple expressions are given (e.g. a vector and a matrix). This means that this should be a non-breaking change.