Remove SymPy dependency
Issue #8
new
Using SymPy in FIAT is like using a sledgehammer to crack a peanut.
It's a heavy dependency that imports a lot of files, which can slow import.
Also, the SymPy Debian/Ubuntu packages pull in a lot of very large package dependencies, e.g. texlive. This blows out the size of virtual images and Linux containers.
Comments (5)
-
-
One option may be to use pymbolic (http://mathema.tician.de/software/pymbolic/) which is quite lightweight. The following patch to expansions.py appears to DTRT:
diff --git a/FIAT/expansions.py b/FIAT/expansions.py index cd22748..ddb3110 100644 --- a/FIAT/expansions.py +++ b/FIAT/expansions.py @@ -21,14 +21,19 @@ to allow users to get coordinates that they want.""" import numpy import math -import sympy +import pymbolic from . import reference_element from . import jacobi -def _tabulate_dpts(tabulator, D, n, order, pts): - X = sympy.DeferredVector('x') +class Differentiator(pymbolic.mapper.differentiator.DifferentiationMapper): + + # Derivative of list is just list of derivative of entries + def map_list(self, expr): + return [self.rec(x) for x in expr] + +def _tabulate_dpts(tabulator, D, n, order, pts): def form_derivative(F): '''Forms the derivative recursively, i.e., F -> [F_x, F_y, F_z], @@ -37,45 +42,10 @@ def _tabulate_dpts(tabulator, D, n, order, pts): [F_zx, F_zy, F_zz]] and so forth. ''' - out = [] - try: - out = [sympy.diff(F, X[j]) for j in range(D)] - except AttributeError: - # Intercept errors like - # AttributeError: 'list' object has no attribute - # 'free_symbols' - for f in F: - out.append(form_derivative(f)) - return out - - def numpy_lambdify(X, F): - '''Unfortunately, SymPy's own lambdify() doesn't work well with - NumPy in that simple functions like - lambda x: 1.0, - when evaluated with NumPy arrays, return just "1.0" instead of - an array of 1s with the same shape as x. This function does that. - ''' - try: - lambda_x = [numpy_lambdify(X, f) for f in F] - except TypeError: # 'function' object is not iterable - # SymPy's lambdify also works on functions that return arrays. - # However, use it componentwise here so we can add 0*x to each - # component individually. This is necessary to maintain shapes - # if evaluated with NumPy arrays. - lmbd_tmp = sympy.lambdify(X, F) - lambda_x = lambda x: lmbd_tmp(x) + 0*x[0] - return lambda_x - - def evaluate_lambda(lmbd, x): - '''Properly evaluate lambda expressions recursively for iterables. - ''' - try: - values = [evaluate_lambda(l, x) for l in lmbd] - except TypeError: # 'function' object is not iterable - values = lmbd(x) - return values + return [Differentiator(X[j])(F) for j in range(D)] # Tabulate symbolically + X = pymbolic.primitives.Variable('x') symbolic_tab = tabulator(n, X) # Make sure that the entries of symbolic_tab are lists so we can # append derivatives @@ -86,10 +56,8 @@ def _tabulate_dpts(tabulator, D, n, order, pts): shape = [len(symbolic_tab), len(pts)] + r * [D] data[r] = numpy.empty(shape) for i, phi in enumerate(symbolic_tab): - # Evaluate the function numerically using lambda expressions - deriv_lambda = numpy_lambdify(X, phi[r]) - data[r][i] = \ - numpy.array(evaluate_lambda(deriv_lambda, pts.T)).T + # Evalute numerically, binding 'x' to the required points + data[r][i] = numpy.array(pymbolic.evaluate(phi[r], {'x': pts.T})).T # Symbolically compute the next derivative. # This actually happens once too many here; never mind for # now.
-
reporter Is pymbolic available as a Debian/Ubuntu package, and does it work with Python 3?
-
It does, I believe, work with python 3 (at least setup.py claims so). It does not appear to be an ubuntu package in 14.04.
-
Maybe we can use ufl with some minor extensions.
- Log in to comment
It is possible to avoid installing texlive together with SymPy by running
apt-get
with--no-install-recommends
.