Sympy: lambdify such that operations on arrays always result in arrays, also for constants?

sympy lambdify multiple variables
sympy lambdify derivative
sympy lambdify invalid syntax
sympy subs
sympy function
lambdify julia
sympy create function from expression
sympy symbols

I need to evaluate the derivative of functions (f') given by the user in many points. The points are in a list (or numpy.array, pandas.Series...). I obtain the expected value when f' depends on a sympy variable, but not when f' is a constant:

import sympy as sp

f1 = sp.sympify('1')
f2 = sp.sympify('t')

lamb1 = sp.lambdify('t',f1)
lamb2 = sp.lambdify('t',f2)

print(lamb1([1,2,3]))
print(lamb2([1,2,3]))

I obtain:

1
[1, 2, 3]

The second is alright, but I expected that the first would be a list of ones.

These functions are in a matrix and the end result of sympy operations, such as taking derivatives. The exact form of f1 and f2 varies per problem.

lamb1 is a function that returns the constant 1: def lamb1(x): return 1.

lamb2 is a function that returns its argument: def lamb2(x): return x.

So, the output is very well the expected one.

Here is an approach that might work. I changed the test function for f2 to t*t as that was more annoying in my tests (dealing with Pow(t,2)).

import sympy as sp
import numpy as np

f1 = sp.sympify('1')
f2 = sp.sympify('t*t')

def np_lambdify(varname, func):
    lamb = sp.lambdify(varname, func, modules=['numpy'])
    if func.is_constant():
        return lambda t: np.full_like(t, lamb(t))
    else:
        return lambda t: lamb(np.array(t))

lamb1 = np_lambdify('t', f1)
lamb2 = np_lambdify('t', f2)

print(lamb1(1))
print(lamb1([1, 2, 3]))
print(lamb2(2))
print(lamb2([1, 2, 3]))

Outputs:

1
[1 1 1]
4
[1 4 9]

Lambdify — SymPy 1.6.2 documentation, from sympy.utilities.lambdify import lambdify, implemented_function function that will unpack the original arguments so that nested arguments can be handled: f(x) then [x] should be the first argument to lambdify ; for this case a single x can also be used: If it is a matrix, an array will be returned (for the NumPy module). I'm trying to lambdify big analytic expression with sp.Max(x, 0) inside. I want to use numpy to vectorize my calculations, so x is going to be an array. I need element-wise maximum values of x and 0.

With isympy/ipython introspection:

In [28]: lamb2??                                                                                 
Signature: lamb2(t)
Docstring:
Created with lambdify. Signature:

func(arg_0)

Expression:

t

Source code:

def _lambdifygenerated(t):
    return (t)

and for the first:

In [29]: lamb1??                                                                                 
Signature: lamb1(t)
Docstring:
Created with lambdify. Signature:

func(arg_0)

Expression:

1

Source code:

def _lambdifygenerated(t):
    return (1)

So one returns the input argument; the other returns just the constant, regardless of the input. lambdify does a rather simple lexical translation from sympy to numpy Python.

edit

Putting your functions in a sp.Matrix:

In [55]: lamb3 = lambdify('t',Matrix([f1,f2]))                                                   

In [56]: lamb3??                                                                                 
...
def _lambdifygenerated(t):
    return (array([[1], [t]]))
...

In [57]: lamb3(np.arange(3))                                                                     
Out[57]: 
array([[1],
       [array([0, 1, 2])]], dtype=object)

So this returns a numpy array; but because of the mix of shapes the result is object dtype, not 2d.

We can see this with a direct array generation:

In [53]: np.array([[1],[1,2,3]])                                                                 
Out[53]: array([list([1]), list([1, 2, 3])], dtype=object)

In [54]: np.array([np.ones(3,int),[1,2,3]])                                                      
Out[54]: 
array([[1, 1, 1],
       [1, 2, 3]])

Neither sympy nor the np.array attempts to 'broadcast' that constant. There are numpy constructs that will do that, such as multiplication and addition, but this simple sympy function and lambdify don't.

edit

frompyfunc is a way of passing an array (or arrays) to a function that only works with scalar inputs. While lamb2 works with an array input, you aren't happy with the lamb1 case, or presumably lamb3.

In [60]: np.frompyfunc(lamb1,1,1)([1,2,3])                                                       
Out[60]: array([1, 1, 1], dtype=object)

In [61]: np.frompyfunc(lamb2,1,1)([1,2,3])                                                       
Out[61]: array([1, 2, 3], dtype=object)

This [61] is slower than simply lamb2([1,2,3]) since it effectively iterates.

In [62]: np.frompyfunc(lamb3,1,1)([1,2,3])                                                       
Out[62]: 
array([array([[1],
       [1]]), array([[1],
       [2]]),
       array([[1],
       [3]])], dtype=object)

In this Matrix case the result is an array of arrays. But since shapes match they can be combined into one array (in various ways):

In [66]: np.concatenate(_62, axis=1)                                                             
Out[66]: 
array([[1, 1, 1],
       [1, 2, 3]])

lambdify of constant functions � Issue #5642 � sympy/sympy � GitHub, Using sympy.lambdify together with numpy arrays has a severe issue when the lambdified function is a constant. where u.shape is (10,) and the result So the function is essentially a constant independent of x. But consider also that expr in lambdify may be multiple expressions with varying shape (e.g. a tuple of a� # Get sizes of arrays and base offsets for each array var_sizes = self.array_sizes(in_bytes=True, subs_consts=True) base_offsets = {} base = 0 # Always arange arrays in alphabetical order in memory, for reproducability for var_name, var_size in sorted(var_sizes.items(), key=lambda v: v[0]): base_offsets[var_name] = base array_total_size = self

I often use the trick t * 0 + 1 to create a zero-vector the same length as my input, but then add 1 to each of its elements. It works with NumPy; check if it works with Sympy!

Lambdify — SymPy Tutorial, If you want to view the lambdified function or provide 'sympy' as the module, you should probably set dummify=False. For functions involving large array� 6 Lab 10. Introduction to SymPy By default, sy.lambdify() uses the math module to convert an expression to a function. orF example, sy.sin() is converted to math.sin() . By providing "numpy" as an additional argument, sy.lambdify() replaces symbolic functions with their NumPy equialenvts instead, so sy.sin() is converted to np.sin() .

I never use lambdify so I can't be too critical of how it is working. But it appears that you will need to fool it by giving it an expression that doesn't simplify to a scalar which, when evaluated with numbers will reduce to the desired value:

>>> import numpy as np
>>> lambdify('t','(1+t)*t-t**2-t+42','numpy')(np.array([1,2,3]))
array([42, 42, 42])

sympy.utilities.lambdify — SymPy 1.4 documentation, This module provides convenient functions to transform sympy In general, SymPy functions do not work with objects from other libraries, such as NumPy arrays, and In the second, we got a numeric result, because ``sin_cos`` used the This is also why the symbols argument is first in ``lambdify``,� It also appears in numpy as numpy.sin, where it can act on vectors and arrays in one go. sympy re-implements many mathematical functions, for example as sympy.sin, which can act on abstract (sympy) variables. Whenever using sympy we should use sympy functions, as these can be manipulated and simplified. For example:

[PDF] Introduction to SymPy, represents a mathematical symbol, such as x or θ, not a number or another kind of data. Operating on symbolic variables results in an expression, representative of an Always use SymPy functions and constants when creating expressions This allows the resulting function to act element-wise on NumPy arrays, not. Teams. Q&A for Work. Stack Overflow for Teams is a private, secure spot for you and your coworkers to find and share information.

Symbolic Python — Maths with Python 1.0 documentation, There are also freely available packages, such as SageMath and sympy . These are not always easy to use, as all CAS have their own formal languages Once we have variables, we can define new variables by operating on old ones: It also appears in numpy as numpy.sin , where it can act on vectors and arrays in one� Sympy: lambdify such that operations on arrays always result in arrays, also for constants? Redirecting subdomain to folder in Rails 3;

Vectors, Matrices, and Multidimensional Arrays, must always be obtained from Springer. Permissions high-level programming language and environment such as Python and its scientific computing libraries. A solution An already created array can also be filled with constant values using the np.fill function, which takes operation using the function sympy. lambdify. In my previous post I demonstrated array operations based on the new functionality I have implemented during my GSOC project. In this post I will discuss another feature that is essential for array calculations in a computer algebra system: The initialization of numeric arrays based on symbolic math expressions.

Comments
  • f1 and f2 are the derivative of a function that is given by the user like I said, the prurpose of using Sympy is to avoid that the user has to derivate and define functions. Also, the number of derivates depends on the problem, your solution is not the general solution.
  • I understand what are you saying, thanks. However, in the script where I use it, as I mentioned, the two evaluations are made in a matrix (the functions are the gradient of another function). So when I evaluate, I obtain something like [1,[1,2,3]], but I need [[1,1,1],[1,2,3]], is there anyway for identify them and use someting like [1]*len([1,2,3]) to obtain the desired [1,1,1] in the case of lamb1?
  • frompyfunc might be useful. It won't be fast, but for your general case it may be the cleanest solution.
  • Your solution is useful, but I prefer the solution of @JohanC (I don't know which is the best or the fastest)
  • f1 and f2 are calculated by sympy.diff (I omitted it in the question), I can't modificate them.