@@ -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) nogil:
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) nogil:
4572
+ cdef symengine.LLVMDoubleVisitor* 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,17 @@ 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 ctypes import c_double, c_void_p, c_int, cast, POINTER, CFUNCTYPE
4609
+ if not self .real:
4610
+ raise RuntimeError (" Lambda function has to be real" )
4611
+ if self .tot_out_size > 1 :
4612
+ raise RuntimeError (" SciPy LowLevelCallable supports only functions with 1 output" )
4613
+ addr1 = cast(< size_t> & _scipy_callback_lambda_real,
4614
+ CFUNCTYPE(c_double, c_int, POINTER(c_double), c_void_p))
4615
+ addr2 = cast(< size_t> & self .lambda_double[0 ], c_void_p)
4616
+ return create_low_level_callable(self , addr1, addr2)
4617
+
4582
4618
4583
4619
IF HAVE_SYMENGINE_LLVM:
4584
4620
cdef class LLVMDouble(_Lambdify):
@@ -4592,8 +4628,19 @@ IF HAVE_SYMENGINE_LLVM:
4592
4628
cpdef unsafe_real(self , double [::1 ] inp, double [::1 ] out, int inp_offset = 0 , int out_offset = 0 ):
4593
4629
self .lambda_double[0 ].call(& out[out_offset], & inp[inp_offset])
4594
4630
4631
+ cpdef as_scipy_low_level_callable(self ):
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
+ if self .tot_out_size > 1 :
4636
+ raise RuntimeError (" SciPy LowLevelCallable supports only functions with 1 output" )
4637
+ addr1 = cast(< size_t> & _scipy_callback_lambda_real,
4638
+ CFUNCTYPE(c_double, c_int, POINTER(c_double), c_void_p))
4639
+ addr2 = cast(< size_t> & self .lambda_double[0 ], c_void_p)
4640
+ return create_low_level_callable(self , addr1, addr2)
4641
+
4595
4642
4596
- def Lambdify (args , *exprs , cppbool real = True , backend = None , order = ' C' ):
4643
+ def Lambdify (args , *exprs , cppbool real = True , backend = None , order = ' C' , as_scipy = False ):
4597
4644
"""
4598
4645
Lambdify instances are callbacks that numerically evaluate their symbolic
4599
4646
expressions from user provided input (real or complex) into (possibly user
@@ -4616,6 +4663,9 @@ def Lambdify(args, *exprs, cppbool real=True, backend=None, order='C'):
4616
4663
(k, l, 3) (C-contiguous) input will give a (k, l, m, n) shaped output,
4617
4664
whereas a (3, k, l) (C-contiguous) input will give a (m, n, k, l) shaped
4618
4665
output. If ``None`` order is taken as ``self.order`` (from initialization).
4666
+ as_scipy : bool
4667
+ return a SciPy LowLevelCallable which can be used in SciPy's integrate
4668
+ methods
4619
4669
4620
4670
Returns
4621
4671
-------
@@ -4637,15 +4687,21 @@ def Lambdify(args, *exprs, cppbool real=True, backend=None, order='C'):
4637
4687
backend = os.getenv(' SYMENGINE_LAMBDIFY_BACKEND' , " lambda" )
4638
4688
if backend == " llvm" :
4639
4689
IF HAVE_SYMENGINE_LLVM:
4640
- return LLVMDouble(args, * exprs, real = real, order = order)
4690
+ ret = LLVMDouble(args, * exprs, real = real, order = order)
4691
+ if as_scipy:
4692
+ return ret.as_scipy_low_level_callable()
4693
+ return ret
4641
4694
ELSE :
4642
4695
raise ValueError (""" llvm backend is chosen, but symengine is not compiled
4643
4696
with llvm support.""" )
4644
4697
elif backend == " lambda" :
4645
4698
pass
4646
4699
else :
4647
4700
warnings.warn(" Unknown SymEngine backend: %s \n Using backend='lambda'" % backend)
4648
- return LambdaDouble(args, * exprs, real = real, order = order)
4701
+ ret = LambdaDouble(args, * exprs, real = real, order = order)
4702
+ if as_scipy:
4703
+ return ret.as_scipy_low_level_callable()
4704
+ return ret
4649
4705
4650
4706
4651
4707
def LambdifyCSE (args , *exprs , cse = None , order = ' C' , **kwargs ):
0 commit comments