From a2c037935d591a7e0c4e0b46fc06087f30df9518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Fri, 17 Nov 2023 10:57:10 +0100 Subject: [PATCH 01/10] minimal set of changes for returning failure to find non-zero pivot in FFGJ --- symengine/lib/symengine.pxd | 2 +- symengine/lib/symengine_wrapper.in.pyx | 4 +++- symengine/tests/test_matrices.py | 11 ++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/symengine/lib/symengine.pxd b/symengine/lib/symengine.pxd index ee96731b..f4d4b6fa 100644 --- a/symengine/lib/symengine.pxd +++ b/symengine/lib/symengine.pxd @@ -687,7 +687,7 @@ cdef extern from "" namespace "SymEngine": DenseMatrix &B) except+ nogil void FFLU_solve "SymEngine::fraction_free_LU_solve"(const DenseMatrix &A, const DenseMatrix &b, DenseMatrix &x) except+ nogil - void FFGJ_solve "SymEngine::fraction_free_gauss_jordan_solve"(const DenseMatrix &A, + int FFGJ_solve "SymEngine::fraction_free_gauss_jordan_solve"(const DenseMatrix &A, const DenseMatrix &b, DenseMatrix &x) except+ nogil void LDL_solve "SymEngine::LDL_solve"(const DenseMatrix &A, const DenseMatrix &b, DenseMatrix &x) except+ nogil diff --git a/symengine/lib/symengine_wrapper.in.pyx b/symengine/lib/symengine_wrapper.in.pyx index e10d1476..b970ad5a 100644 --- a/symengine/lib/symengine_wrapper.in.pyx +++ b/symengine/lib/symengine_wrapper.in.pyx @@ -3998,9 +3998,11 @@ cdef class DenseMatrixBase(MatrixBase): deref(symengine.static_cast_DenseMatrix(b_.thisptr)), deref(symengine.static_cast_DenseMatrix(x.thisptr))) elif method.upper() == 'FFGJ': - symengine.FFGJ_solve(deref(symengine.static_cast_DenseMatrix(self.thisptr)), + failure = symengine.FFGJ_solve(deref(symengine.static_cast_DenseMatrix(self.thisptr)), deref(symengine.static_cast_DenseMatrix(b_.thisptr)), deref(symengine.static_cast_DenseMatrix(x.thisptr))) + if (failure != 0): + raise Exception("Underdetermined system. Failed to find non-zero pivot in column: %d" % failure) else: raise Exception("Unsupported method.") diff --git a/symengine/tests/test_matrices.py b/symengine/tests/test_matrices.py index de997529..b0a10a2e 100644 --- a/symengine/tests/test_matrices.py +++ b/symengine/tests/test_matrices.py @@ -11,7 +11,7 @@ have_numpy = True except ImportError: have_numpy = False - + try: import sympy from sympy.core.cache import clear_cache @@ -444,6 +444,15 @@ def test_solve(): x = A.solve(b, 'FFGJ') assert x == y + B = DenseMatrix(2, 2, [0]*4) + try: + B.solve(b, 'FFGJ') + except Exception: + pass + else: + raise Exception("this operation should have raised an exception") + + def test_FFLU(): A = DenseMatrix(4, 4, [1, 2, 3, 4, 2, 2, 3, 4, 3, 3, 3, 4, 9, 8, 7, 6]) From 345591b9c59a14f241a9406960ce9c5410038c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Fri, 17 Nov 2023 21:16:47 +0100 Subject: [PATCH 02/10] add DenseMatrixBase.__abs__ --- symengine/lib/symengine_wrapper.in.pyx | 3 +++ symengine/tests/test_matrices.py | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/symengine/lib/symengine_wrapper.in.pyx b/symengine/lib/symengine_wrapper.in.pyx index b970ad5a..15993fab 100644 --- a/symengine/lib/symengine_wrapper.in.pyx +++ b/symengine/lib/symengine_wrapper.in.pyx @@ -3529,6 +3529,9 @@ cdef class DenseMatrixBase(MatrixBase): def __neg__(self): return self.mul_scalar(-1) + def __abs__(self): + return self.applyfunc(abs) + def __getitem__(self, item): if isinstance(item, slice): if (self.ncols() == 0 or self.nrows() == 0): diff --git a/symengine/tests/test_matrices.py b/symengine/tests/test_matrices.py index b0a10a2e..5e6821c4 100644 --- a/symengine/tests/test_matrices.py +++ b/symengine/tests/test_matrices.py @@ -286,11 +286,13 @@ def test_mul_scalar(): assert a * A == DenseMatrix(2, 2, [a, 2*a, 3*a, 4*a]) -def test_neg(): +def test_neg_abs(): A = DenseMatrix(2, 3, [1, 2, 3, 4, 5, 6]) B = DenseMatrix(2, 3, [-1, -2, -3, -4, -5, -6]) assert -A == B + assert A == abs(B) + def test_sub(): A = DenseMatrix(2, 2, [1, 2, 3, 4]) From 4bcebf94666b6053880836074ca4d80a833f71fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Fri, 8 Mar 2024 10:43:53 +0100 Subject: [PATCH 03/10] compatibility: make lambdify more sympy-like --- symengine/__init__.py | 9 +++++---- symengine/utilities.py | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/symengine/__init__.py b/symengine/__init__.py index b6002a28..1eabb110 100644 --- a/symengine/__init__.py +++ b/symengine/__init__.py @@ -52,14 +52,15 @@ from .lib.symengine_wrapper import ComplexMPC if have_numpy: - from .lib.symengine_wrapper import (Lambdify, LambdifyCSE) - + from .lib.symengine_wrapper import Lambdify, LambdifyCSE def lambdify(args, exprs, **kwargs): + if isinstance(exprs, Basic): + exprs = [exprs] return Lambdify(args, *exprs, **kwargs) else: def __getattr__(name): - if name == 'lambdify': - raise AttributeError("Cannot import numpy, which is required for `lambdify` to work") + if name in ('lambdify', 'Lambdify', 'LambdifyCSE'): + raise AttributeError(f"Cannot import numpy, which is required for `{name}` to work") raise AttributeError(f"module 'symengine' has no attribute '{name}'") diff --git a/symengine/utilities.py b/symengine/utilities.py index a64ea15e..1d186707 100644 --- a/symengine/utilities.py +++ b/symengine/utilities.py @@ -1,4 +1,5 @@ from .lib.symengine_wrapper import Symbol, Basic + from itertools import combinations, permutations, product, product as cartes import re as _re import string From f6426fc4019a1edc2d8e61d4bb91ad72f14250ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Mon, 27 May 2024 21:55:22 +0200 Subject: [PATCH 04/10] Fix cython syntax, add tests --- symengine/lib/symengine_wrapper.in.pyx | 8 ++++++-- symengine/tests/test_logic.py | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/symengine/lib/symengine_wrapper.in.pyx b/symengine/lib/symengine_wrapper.in.pyx index e10d1476..c29c8978 100644 --- a/symengine/lib/symengine_wrapper.in.pyx +++ b/symengine/lib/symengine_wrapper.in.pyx @@ -1627,7 +1627,9 @@ class Equality(Relational): def is_Equality(self): return True - func = __class__ + @property + def func(self): + return self.__class__ Eq = Equality @@ -1648,7 +1650,9 @@ class Unequality(Relational): s = self.args_as_sage() return sage.ne(*s) - func = __class__ + @property + def func(self): + return self.__class__ Ne = Unequality diff --git a/symengine/tests/test_logic.py b/symengine/tests/test_logic.py index 7c01f8e9..3d0c3391 100644 --- a/symengine/tests/test_logic.py +++ b/symengine/tests/test_logic.py @@ -27,6 +27,10 @@ def test_relationals(): assert Ge(1, 1) == true assert Eq(I, 2) == false assert Ne(I, 2) == true + eq = Eq(x, y) + assert eq.func(*eq.args) == eq + ne = Ne(x, y) + assert ne.func(*ne.args) == ne def test_rich_cmp(): @@ -118,4 +122,3 @@ def test_Contains(): assert Contains(x, Interval(1, 1)) != false assert Contains(oo, Interval(-oo, oo)) == false assert Contains(-oo, Interval(-oo, oo)) == false - From 7472cce6f326ad53ca25ccc541198497cc2edb65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Fri, 31 May 2024 11:16:29 +0200 Subject: [PATCH 05/10] long is not a built-in --- symengine/lib/symengine_wrapper.in.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/symengine/lib/symengine_wrapper.in.pyx b/symengine/lib/symengine_wrapper.in.pyx index c29c8978..34c11e9f 100644 --- a/symengine/lib/symengine_wrapper.in.pyx +++ b/symengine/lib/symengine_wrapper.in.pyx @@ -1211,7 +1211,7 @@ cdef class Basic(object): return int(float(self)) def __long__(self): - return long(float(self)) + return int(float(self)) def __complex__(self): f = self.n(real=False) From dd3dc6efc228c9193088e3b284fa88faf920b393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Fri, 31 May 2024 11:41:58 +0200 Subject: [PATCH 06/10] vector lambda_double -> unique_ptr lambda_visitor --- symengine/lib/symengine_wrapper.in.pxd | 11 +++-- symengine/lib/symengine_wrapper.in.pyx | 66 +++++++++++++------------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/symengine/lib/symengine_wrapper.in.pxd b/symengine/lib/symengine_wrapper.in.pxd index 3712e17c..03dbf1f0 100644 --- a/symengine/lib/symengine_wrapper.in.pxd +++ b/symengine/lib/symengine_wrapper.in.pxd @@ -2,6 +2,7 @@ cimport symengine from symengine cimport RCP, map_basic_basic, rcp_const_basic +from libcpp.memory cimport unique_ptr from libcpp.vector cimport vector from libcpp.string cimport string from libcpp cimport bool as cppbool @@ -44,7 +45,7 @@ cdef class _Lambdify(object): cpdef unsafe_eval(sef, inp, out, unsigned nbroadcast=*) cdef class LambdaDouble(_Lambdify): - cdef vector[symengine.LambdaRealDoubleVisitor] lambda_double + cdef unique_ptr[symengine.LambdaRealDoubleVisitor] lambda_visitor cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse) cpdef unsafe_real(self, double[::1] inp, double[::1] out, int inp_offset=*, int out_offset=*) cpdef as_scipy_low_level_callable(self) @@ -54,7 +55,7 @@ cdef class LambdaDouble(_Lambdify): int inp_offset=*, int out_offset=*) cdef class LambdaComplexDouble(_Lambdify): - cdef vector[symengine.LambdaComplexDoubleVisitor] lambda_double + cdef unique_ptr[symengine.LambdaComplexDoubleVisitor] lambda_visitor cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse) cpdef unsafe_complex(self, double complex[::1] inp, double complex[::1] out, int inp_offset=*, int out_offset=*) @@ -63,7 +64,7 @@ IF HAVE_SYMENGINE_LLVM: cdef int opt_level cdef class LLVMDouble(_LLVMLambdify): - cdef vector[symengine.LLVMDoubleVisitor] lambda_double + cdef unique_ptr[symengine.LLVMDoubleVisitor] lambda_visitor cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse) cdef _load(self, const string &s) cpdef unsafe_real(self, double[::1] inp, double[::1] out, int inp_offset=*, int out_offset=*) @@ -71,14 +72,14 @@ IF HAVE_SYMENGINE_LLVM: cpdef as_ctypes(self) cdef class LLVMFloat(_LLVMLambdify): - cdef vector[symengine.LLVMFloatVisitor] lambda_double + cdef unique_ptr[symengine.LLVMFloatVisitor] lambda_visitor cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse) cdef _load(self, const string &s) cpdef unsafe_real(self, float[::1] inp, float[::1] out, int inp_offset=*, int out_offset=*) IF HAVE_SYMENGINE_LLVM_LONG_DOUBLE: cdef class LLVMLongDouble(_LLVMLambdify): - cdef vector[symengine.LLVMLongDoubleVisitor] lambda_double + cdef unique_ptr[symengine.LLVMLongDoubleVisitor] lambda_visitor cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse) cdef _load(self, const string &s) cpdef unsafe_real(self, long double[::1] inp, long double[::1] out, int inp_offset=*, int out_offset=*) diff --git a/symengine/lib/symengine_wrapper.in.pyx b/symengine/lib/symengine_wrapper.in.pyx index 34c11e9f..44688fbe 100644 --- a/symengine/lib/symengine_wrapper.in.pyx +++ b/symengine/lib/symengine_wrapper.in.pyx @@ -5156,11 +5156,11 @@ cdef class LambdaDouble(_Lambdify): pass cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse): - self.lambda_double.resize(1) - self.lambda_double[0].init(args_, outs_, cse) + self.lambda_visitor.reset(new symengine.LambdaRealDoubleVisitor()) + deref(self.lambda_visitor).init(args_, outs_, cse) cpdef unsafe_real(self, double[::1] inp, double[::1] out, int inp_offset=0, int out_offset=0): - self.lambda_double[0].call(&out[out_offset], &inp[inp_offset]) + deref(self.lambda_visitor).call(&out[out_offset], &inp[inp_offset]) cpdef unsafe_eval(self, inp, out, unsigned nbroadcast=1): cdef double[::1] c_inp, c_out @@ -5168,7 +5168,7 @@ cdef class LambdaDouble(_Lambdify): c_inp = np.ascontiguousarray(inp.ravel(order=self.order), dtype=self.numpy_dtype) c_out = out for idx in range(nbroadcast): - self.lambda_double[0].call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) + deref(self.lambda_visitor).call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) cpdef as_scipy_low_level_callable(self): from ctypes import c_double, c_void_p, c_int, cast, POINTER, CFUNCTYPE @@ -5176,7 +5176,7 @@ cdef class LambdaDouble(_Lambdify): raise RuntimeError("SciPy LowLevelCallable supports only functions with 1 output") addr1 = cast(&_scipy_callback_lambda_real, CFUNCTYPE(c_double, c_int, POINTER(c_double), c_void_p)) - addr2 = cast(&self.lambda_double[0], c_void_p) + addr2 = cast(self.lambda_visitor.get(), c_void_p) return create_low_level_callable(self, addr1, addr2) cpdef as_ctypes(self): @@ -5191,7 +5191,7 @@ cdef class LambdaDouble(_Lambdify): from ctypes import c_double, c_void_p, c_int, cast, POINTER, CFUNCTYPE addr1 = cast(&_ctypes_callback_lambda_real, CFUNCTYPE(c_void_p, POINTER(c_double), POINTER(c_double), c_void_p)) - addr2 = cast(&self.lambda_double[0], c_void_p) + addr2 = cast(self.lambda_visitor.get(), c_void_p) return addr1, addr2 @@ -5201,11 +5201,11 @@ cdef class LambdaComplexDouble(_Lambdify): pass cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse): - self.lambda_double.resize(1) - self.lambda_double[0].init(args_, outs_, cse) + self.lambda_visitor.reset(new symengine.LambdaComplexDoubleVisitor()) + deref(self.lambda_visitor).init(args_, outs_, cse) cpdef unsafe_complex(self, double complex[::1] inp, double complex[::1] out, int inp_offset=0, int out_offset=0): - self.lambda_double[0].call(&out[out_offset], &inp[inp_offset]) + deref(self.lambda_visitor).call(&out[out_offset], &inp[inp_offset]) cpdef unsafe_eval(self, inp, out, unsigned nbroadcast=1): cdef double complex[::1] c_inp, c_out @@ -5213,7 +5213,7 @@ cdef class LambdaComplexDouble(_Lambdify): c_inp = np.ascontiguousarray(inp.ravel(order=self.order), dtype=self.numpy_dtype) c_out = out for idx in range(nbroadcast): - self.lambda_double[0].call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) + deref(self.lambda_visitor).call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) IF HAVE_SYMENGINE_LLVM: @@ -5222,23 +5222,23 @@ IF HAVE_SYMENGINE_LLVM: self.opt_level = opt_level cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse): - self.lambda_double.resize(1) - self.lambda_double[0].init(args_, outs_, cse, self.opt_level) + self.lambda_visitor.reset(new symengine.LLVMDoubleVisitor()) + deref(self.lambda_visitor).init(args_, outs_, cse, self.opt_level) cdef _load(self, const string &s): - self.lambda_double.resize(1) - self.lambda_double[0].loads(s) + self.lambda_visitor.reset(new symengine.LLVMDoubleVisitor()) + deref(self.lambda_visitor).loads(s) def __reduce__(self): """ Interface for pickle. Note that the resulting object is platform dependent. """ - cdef bytes s = self.lambda_double[0].dumps() + cdef bytes s = deref(self.lambda_visitor).dumps() return llvm_loading_func, (self.args_size, self.tot_out_size, self.out_shapes, self.real, \ self.n_exprs, self.order, self.accum_out_sizes, self.numpy_dtype, s) cpdef unsafe_real(self, double[::1] inp, double[::1] out, int inp_offset=0, int out_offset=0): - self.lambda_double[0].call(&out[out_offset], &inp[inp_offset]) + deref(self.lambda_visitor).call(&out[out_offset], &inp[inp_offset]) cpdef unsafe_eval(self, inp, out, unsigned nbroadcast=1): cdef double[::1] c_inp, c_out @@ -5246,7 +5246,7 @@ IF HAVE_SYMENGINE_LLVM: c_inp = np.ascontiguousarray(inp.ravel(order=self.order), dtype=self.numpy_dtype) c_out = out for idx in range(nbroadcast): - self.lambda_double[0].call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) + deref(self.lambda_visitor).call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) cpdef as_scipy_low_level_callable(self): from ctypes import c_double, c_void_p, c_int, cast, POINTER, CFUNCTYPE @@ -5256,7 +5256,7 @@ IF HAVE_SYMENGINE_LLVM: raise RuntimeError("SciPy LowLevelCallable supports only functions with 1 output") addr1 = cast(&_scipy_callback_llvm_real, CFUNCTYPE(c_double, c_int, POINTER(c_double), c_void_p)) - addr2 = cast(&self.lambda_double[0], c_void_p) + addr2 = cast(self.lambda_visitor.get(), c_void_p) return create_low_level_callable(self, addr1, addr2) cpdef as_ctypes(self): @@ -5273,7 +5273,7 @@ IF HAVE_SYMENGINE_LLVM: raise RuntimeError("Lambda function has to be real") addr1 = cast(&_ctypes_callback_llvm_real, CFUNCTYPE(c_void_p, POINTER(c_double), POINTER(c_double), c_void_p)) - addr2 = cast(&self.lambda_double[0], c_void_p) + addr2 = cast(self.lambda_visitor.get(), c_void_p) return addr1, addr2 cdef class LLVMFloat(_LLVMLambdify): @@ -5281,23 +5281,23 @@ IF HAVE_SYMENGINE_LLVM: self.opt_level = opt_level cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse): - self.lambda_double.resize(1) - self.lambda_double[0].init(args_, outs_, cse, self.opt_level) + self.lambda_visitor.reset(new symengine.LLVMFloatVisitor()) + deref(self.lambda_visitor).init(args_, outs_, cse, self.opt_level) cdef _load(self, const string &s): - self.lambda_double.resize(1) - self.lambda_double[0].loads(s) + self.lambda_visitor.reset(new symengine.LLVMFloatVisitor()) + deref(self.lambda_visitor).loads(s) def __reduce__(self): """ Interface for pickle. Note that the resulting object is platform dependent. """ - cdef bytes s = self.lambda_double[0].dumps() + cdef bytes s = deref(self.lambda_visitor).dumps() return llvm_float_loading_func, (self.args_size, self.tot_out_size, self.out_shapes, self.real, \ self.n_exprs, self.order, self.accum_out_sizes, self.numpy_dtype, s) cpdef unsafe_real(self, float[::1] inp, float[::1] out, int inp_offset=0, int out_offset=0): - self.lambda_double[0].call(&out[out_offset], &inp[inp_offset]) + deref(self.lambda_visitor).call(&out[out_offset], &inp[inp_offset]) cpdef unsafe_eval(self, inp, out, unsigned nbroadcast=1): cdef float[::1] c_inp, c_out @@ -5305,7 +5305,7 @@ IF HAVE_SYMENGINE_LLVM: c_inp = np.ascontiguousarray(inp.ravel(order=self.order), dtype=self.numpy_dtype) c_out = out for idx in range(nbroadcast): - self.lambda_double[0].call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) + deref(self.lambda_visitor).call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) IF HAVE_SYMENGINE_LLVM_LONG_DOUBLE: cdef class LLVMLongDouble(_LLVMLambdify): @@ -5313,23 +5313,23 @@ IF HAVE_SYMENGINE_LLVM: self.opt_level = opt_level cdef _init(self, symengine.vec_basic& args_, symengine.vec_basic& outs_, cppbool cse): - self.lambda_double.resize(1) - self.lambda_double[0].init(args_, outs_, cse, self.opt_level) + self.lambda_visitor.reset(new symengine.LLVMLongDoubleVisitor()) + deref(self.lambda_visitor).init(args_, outs_, cse, self.opt_level) cdef _load(self, const string &s): - self.lambda_double.resize(1) - self.lambda_double[0].loads(s) + self.lambda_visitor.reset(new symengine.LLVMLongDoubleVisitor()) + deref(self.lambda_visitor).loads(s) def __reduce__(self): """ Interface for pickle. Note that the resulting object is platform dependent. """ - cdef bytes s = self.lambda_double[0].dumps() + cdef bytes s = deref(self.lambda_visitor).dumps() return llvm_long_double_loading_func, (self.args_size, self.tot_out_size, self.out_shapes, self.real, \ self.n_exprs, self.order, self.accum_out_sizes, self.numpy_dtype, s) cpdef unsafe_real(self, long double[::1] inp, long double[::1] out, int inp_offset=0, int out_offset=0): - self.lambda_double[0].call(&out[out_offset], &inp[inp_offset]) + deref(self.lambda_visitor).call(&out[out_offset], &inp[inp_offset]) cpdef unsafe_eval(self, inp, out, unsigned nbroadcast=1): cdef long double[::1] c_inp, c_out @@ -5337,7 +5337,7 @@ IF HAVE_SYMENGINE_LLVM: c_inp = np.ascontiguousarray(inp.ravel(order=self.order), dtype=self.numpy_dtype) c_out = out for idx in range(nbroadcast): - self.lambda_double[0].call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) + deref(self.lambda_visitor).call(&c_out[idx*self.tot_out_size], &c_inp[idx*self.args_size]) def llvm_loading_func(*args): return LLVMDouble(args, _load=True) From 4204b2dca364c47d9b32be99bd7e58430d39397e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Fri, 31 May 2024 15:34:33 +0200 Subject: [PATCH 07/10] Try to add work-around for sympy/sympy#26645 --- symengine/lib/symengine_wrapper.in.pyx | 18 ++++++++++++++++++ symengine/tests/test_sympy_conv.py | 1 + 2 files changed, 19 insertions(+) diff --git a/symengine/lib/symengine_wrapper.in.pyx b/symengine/lib/symengine_wrapper.in.pyx index 44688fbe..8fa62c83 100644 --- a/symengine/lib/symengine_wrapper.in.pyx +++ b/symengine/lib/symengine_wrapper.in.pyx @@ -31,6 +31,13 @@ try: except ImportError: have_numpy = False +try: + import flint as _flint + have_flint_py = True +except ImportError: + _flint = None + have_flint_py = False + class SympifyError(Exception): pass @@ -499,6 +506,17 @@ def sympy2symengine(a, raise_error=False): elif isinstance(a, sympy.ConditionSet): return conditionset(*(a.args)) + if have_flint_py: + if isinstance(a, _flint.types.nmod.nmod): + # Work around for sympy/sympy#26645 + class _modular_integer(sympy.polys.domains.modularinteger.ModularInteger): + mod = int(a.modulus()) + dom = sympy.polys.domains.ZZ + sym = True + + b = _modular_integer(int(a)) + return PyNumber(b, sympy_module) + if raise_error: raise SympifyError(("sympy2symengine: Cannot convert '%r' (of type %s)" " to a symengine type.") % (a, type(a))) diff --git a/symengine/tests/test_sympy_conv.py b/symengine/tests/test_sympy_conv.py index 5d173dc4..fbba8179 100644 --- a/symengine/tests/test_sympy_conv.py +++ b/symengine/tests/test_sympy_conv.py @@ -778,6 +778,7 @@ def test_pynumber(): assert isinstance(b, PyNumber) assert b == a # Check equality via SymEngine assert a == b # Check equality via SymPy + assert (b-a).simplify() == 0 assert str(a) == str(b) a = 1 - a From 14cd07d8c81f3c2c220e9fc894927465ca0177a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Fri, 31 May 2024 15:37:21 +0200 Subject: [PATCH 08/10] bump symengine version to v0.12.0 --- symengine_version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/symengine_version.txt b/symengine_version.txt index 4f7638fd..87a1cf59 100644 --- a/symengine_version.txt +++ b/symengine_version.txt @@ -1 +1 @@ -v0.11.1 +v0.12.0 From 1618a867fa40c7f82dc9ccbdc2e711d97365c598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Fri, 31 May 2024 16:33:35 +0200 Subject: [PATCH 09/10] Add python-flint to one CI config --- .github/workflows/ci.yml | 3 ++- bin/install_travis.sh | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6993dd9e..8b9877b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,11 +132,12 @@ jobs: CC: gcc - BUILD_TYPE: Release - PYTHON_VERSION: '3.8' + PYTHON_VERSION: '3.11' OS: ubuntu-20.04 WITH_MPC: yes WITH_MPFR: yes WITH_FLINT: yes + WITH_FLINT_PY: yes WITH_SCIPY: yes WITH_DOCS: yes INTEGER_CLASS: flint diff --git a/bin/install_travis.sh b/bin/install_travis.sh index 581354a0..8176202d 100644 --- a/bin/install_travis.sh +++ b/bin/install_travis.sh @@ -16,6 +16,10 @@ if [[ "${WITH_DOCS}" == "yes" ]]; then export conda_pkgs="${conda_pkgs} sphinx recommonmark"; fi +if [[ "${WITH_FLINT_PY}" == "yes" ]]; then + export conda_pkgs="${conda_pkgs} python-flint"; # python-flint affects sympy, see e.g. sympy/sympy#26645 +fi + if [[ "${WITH_SAGE}" == "yes" ]]; then # This is split to avoid the 10 minute limit conda install -q sagelib=8.1 From c6f95e3a3fe6ca3a1398830fc2e792f7e88794e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Fri, 31 May 2024 16:44:17 +0200 Subject: [PATCH 10/10] Revert adaptions to flint.types.nmod.nmod Following the explanation in https://github.com/sympy/sympy/issues/26645#issuecomment-2142388813 --- symengine/lib/symengine_wrapper.in.pyx | 18 ------------------ symengine/tests/test_sympy_conv.py | 1 - 2 files changed, 19 deletions(-) diff --git a/symengine/lib/symengine_wrapper.in.pyx b/symengine/lib/symengine_wrapper.in.pyx index 8fa62c83..44688fbe 100644 --- a/symengine/lib/symengine_wrapper.in.pyx +++ b/symengine/lib/symengine_wrapper.in.pyx @@ -31,13 +31,6 @@ try: except ImportError: have_numpy = False -try: - import flint as _flint - have_flint_py = True -except ImportError: - _flint = None - have_flint_py = False - class SympifyError(Exception): pass @@ -506,17 +499,6 @@ def sympy2symengine(a, raise_error=False): elif isinstance(a, sympy.ConditionSet): return conditionset(*(a.args)) - if have_flint_py: - if isinstance(a, _flint.types.nmod.nmod): - # Work around for sympy/sympy#26645 - class _modular_integer(sympy.polys.domains.modularinteger.ModularInteger): - mod = int(a.modulus()) - dom = sympy.polys.domains.ZZ - sym = True - - b = _modular_integer(int(a)) - return PyNumber(b, sympy_module) - if raise_error: raise SympifyError(("sympy2symengine: Cannot convert '%r' (of type %s)" " to a symengine type.") % (a, type(a))) diff --git a/symengine/tests/test_sympy_conv.py b/symengine/tests/test_sympy_conv.py index fbba8179..5d173dc4 100644 --- a/symengine/tests/test_sympy_conv.py +++ b/symengine/tests/test_sympy_conv.py @@ -778,7 +778,6 @@ def test_pynumber(): assert isinstance(b, PyNumber) assert b == a # Check equality via SymEngine assert a == b # Check equality via SymPy - assert (b-a).simplify() == 0 assert str(a) == str(b) a = 1 - a