@@ -4560,6 +4560,31 @@ cdef class _Lambdify(object):
4560
4560
return result
4561
4561
4562
4562
4563
+ cdef double _scipy_callback_lambda_real(int n, double * x, void * user_data):
4564
+ cdef symengine.LambdaRealDoubleVisitor* lamb = < symengine.LambdaRealDoubleVisitor * > user_data
4565
+ cdef double result
4566
+ deref(lamb).call(& result, x)
4567
+ return result
4568
+
4569
+
4570
+ IF HAVE_SYMENGINE_LLVM:
4571
+ cdef double _scipy_callback_llvm_real(int n, double * x, void * user_data):
4572
+ cdef symengine.LLVMRealDoubleVisitor* lamb = < symengine.LLVMDoubleVisitor * > user_data
4573
+ cdef double result
4574
+ deref(lamb).call(& result, x)
4575
+ return result
4576
+
4577
+
4578
+ def create_low_level_callable (lambdify , *args ):
4579
+ from scipy import LowLevelCallable
4580
+ class LambdifyLowLevelCallable (LowLevelCallable ):
4581
+ def __init__ (self , lambdify , *args ):
4582
+ self .lambdify = lambdify
4583
+ def __new__ (cls , value , *args , **kwargs ):
4584
+ return super (LambdifyLowLevelCallable, cls ).__new__(cls , * args)
4585
+ return LambdifyLowLevelCallable(lambdify, * args)
4586
+
4587
+
4563
4588
cdef class LambdaDouble(_Lambdify):
4564
4589
4565
4590
cdef vector[symengine.LambdaRealDoubleVisitor] lambda_double
@@ -4579,6 +4604,16 @@ cdef class LambdaDouble(_Lambdify):
4579
4604
cpdef unsafe_complex(self , double complex [::1 ] inp, double complex [::1 ] out, int inp_offset = 0 , int out_offset = 0 ):
4580
4605
self .lambda_double_complex[0 ].call(& out[out_offset], & inp[inp_offset])
4581
4606
4607
+ cpdef as_scipy_low_level_callable(self ):
4608
+ from scipy import LowLevelCallable
4609
+ from ctypes import c_double, c_void_p, c_int, cast, POINTER, CFUNCTYPE
4610
+ if not self .real:
4611
+ raise RuntimeError (" Lambda function has to be real" )
4612
+ addr1 = cast(< size_t> & _scipy_callback_lambda_real,
4613
+ CFUNCTYPE(c_double, c_int, POINTER(c_double), c_void_p))
4614
+ addr2 = cast(< size_t> & self .lambda_double[0 ], c_void_p)
4615
+ return create_low_level_callable(self , addr1, addr2)
4616
+
4582
4617
4583
4618
IF HAVE_SYMENGINE_LLVM:
4584
4619
cdef class LLVMDouble(_Lambdify):
@@ -4592,8 +4627,18 @@ IF HAVE_SYMENGINE_LLVM:
4592
4627
cpdef unsafe_real(self , double [::1 ] inp, double [::1 ] out, int inp_offset = 0 , int out_offset = 0 ):
4593
4628
self .lambda_double[0 ].call(& out[out_offset], & inp[inp_offset])
4594
4629
4630
+ cpdef as_scipy_low_level_callable(self ):
4631
+ from scipy import LowLevelCallable
4632
+ from ctypes import c_double, c_void_p, c_int, cast, POINTER, CFUNCTYPE
4633
+ if not self .real:
4634
+ raise RuntimeError (" Lambda function has to be real" )
4635
+ addr1 = cast(< size_t> & _scipy_callback_lambda_real,
4636
+ CFUNCTYPE(c_double, c_int, POINTER(c_double), c_void_p))
4637
+ addr2 = cast(< size_t> & self .lambda_double[0 ], c_void_p)
4638
+ return create_low_level_callable(self , addr1, addr2)
4639
+
4595
4640
4596
- def Lambdify (args , *exprs , cppbool real = True , backend = None , order = ' C' ):
4641
+ def Lambdify (args , *exprs , cppbool real = True , backend = None , order = ' C' , as_scipy = False ):
4597
4642
"""
4598
4643
Lambdify instances are callbacks that numerically evaluate their symbolic
4599
4644
expressions from user provided input (real or complex) into (possibly user
@@ -4616,6 +4661,9 @@ def Lambdify(args, *exprs, cppbool real=True, backend=None, order='C'):
4616
4661
(k, l, 3) (C-contiguous) input will give a (k, l, m, n) shaped output,
4617
4662
whereas a (3, k, l) (C-contiguous) input will give a (m, n, k, l) shaped
4618
4663
output. If ``None`` order is taken as ``self.order`` (from initialization).
4664
+ as_scipy : bool
4665
+ return a SciPy LowLevelCallable which can be used in SciPy's integrate
4666
+ methods
4619
4667
4620
4668
Returns
4621
4669
-------
@@ -4637,15 +4685,21 @@ def Lambdify(args, *exprs, cppbool real=True, backend=None, order='C'):
4637
4685
backend = os.getenv(' SYMENGINE_LAMBDIFY_BACKEND' , " lambda" )
4638
4686
if backend == " llvm" :
4639
4687
IF HAVE_SYMENGINE_LLVM:
4640
- return LLVMDouble(args, * exprs, real = real, order = order)
4688
+ ret = LLVMDouble(args, * exprs, real = real, order = order)
4689
+ if as_scipy:
4690
+ return ret.as_scipy_low_level_callable()
4691
+ return ret
4641
4692
ELSE :
4642
4693
raise ValueError (""" llvm backend is chosen, but symengine is not compiled
4643
4694
with llvm support.""" )
4644
4695
elif backend == " lambda" :
4645
4696
pass
4646
4697
else :
4647
4698
warnings.warn(" Unknown SymEngine backend: %s \n Using backend='lambda'" % backend)
4648
- return LambdaDouble(args, * exprs, real = real, order = order)
4699
+ ret = LambdaDouble(args, * exprs, real = real, order = order)
4700
+ if as_scipy:
4701
+ return ret.as_scipy_low_level_callable()
4702
+ return ret
4649
4703
4650
4704
4651
4705
def LambdifyCSE (args , *exprs , cse = None , order = ' C' , **kwargs ):
0 commit comments