Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion symengine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
Max, Min, DenseMatrix, Matrix,
ImmutableMatrix, ImmutableDenseMatrix, MutableDenseMatrix,
MatrixBase, Basic, DictBasic, symarray, series, diff, zeros,
eye, diag, ones, Derivative, Subs, expand, has_symbol,
eye, diag, ones, Derivative, Subs, expand, has_basic, has_symbol,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not export has_basic and use .has

UndefFunction, Function, UnevaluatedExpr, latex,
have_numpy, true, false, Equality, Unequality, GreaterThan,
LessThan, StrictGreaterThan, StrictLessThan, Eq, Ne, Ge, Le,
Expand Down
1 change: 1 addition & 0 deletions symengine/lib/symengine.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,7 @@ cdef extern from "<symengine/prime_sieve.h>" namespace "SymEngine":
unsigned next_prime() nogil

cdef extern from "<symengine/visitor.h>" namespace "SymEngine":
bool has_basic(const Basic &b, const Basic &x) nogil except +
bool has_symbol(const Basic &b, const Basic &x) nogil except +
rcp_const_basic coeff(const Basic &b, const Basic &x, const Basic &n) nogil except +
set_basic free_symbols(const Basic &b) nogil except +
Expand Down
12 changes: 10 additions & 2 deletions symengine/lib/symengine_wrapper.in.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1187,8 +1187,11 @@ cdef class Basic(object):
cdef Basic _n = sympify(n)
return c2py(symengine.coeff(deref(self.thisptr), deref(_x.thisptr), deref(_n.thisptr)))

def has(self, *symbols):
return any([has_symbol(self, symbol) for symbol in symbols])
def has(self, *args):
for arg in args:
Copy link
Contributor Author

@bjodah bjodah Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop here begs the question:
Should HasBasicVisitor's looking_for_ rather be a vec_basic instead of a simple rcp_basic? If it's common to query with multiple arguments, then a vec_basic would avoid traversing the (possibly large) syntax tree multiple times, but if the majority of use cases only matches against a single argument, that would be a pessimisation, what do you think?

if has_basic(self, arg):
return True
return False

def args_as_sage(Basic self):
cdef symengine.vec_basic Y = deref(self.thisptr).get_args()
Expand Down Expand Up @@ -4945,6 +4948,11 @@ def powermod_list(a, b, m):
s.append(c2py(<rcp_const_basic>(v[i])))
return s

def has_basic(obj, looking_for=None):
cdef Basic b = _sympify(obj)
cdef Basic s = _sympify(looking_for)
return symengine.has_basic(deref(b.thisptr), deref(s.thisptr))

def has_symbol(obj, symbol=None):
cdef Basic b = _sympify(obj)
cdef Basic s = _sympify(symbol)
Expand Down
16 changes: 16 additions & 0 deletions symengine/tests/test_expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,19 @@ def test_as_powers_dict():
assert (x*(1/Integer(2))**y).as_powers_dict() == {x: Integer(1), Integer(2): -y}
assert (2**y).as_powers_dict() == {2: y}
assert (2**-y).as_powers_dict() == {2: -y}


def test_Basic__has():
x = Symbol('x')
y = Symbol('y')
xp3 = (x+3)
ym4 = (y-4)
e = xp3**ym4
assert e.has(xp3)
assert e.has(ym4)
assert not e.has(y-5)

assert (x + oo).has(oo)
assert (x - oo).has(-oo)
assert not (x + oo).has(-oo)
#assert not (x - oo).has(oo) <-- not sure we want to test explicitly for "x + NegativeInfinity"
1 change: 1 addition & 0 deletions symengine/tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def test_derivative():

fxy = Function("f")(x, y)
assert (1+fxy).has(fxy)
assert (1+fxy).has(1)
g = Derivative(Function("f")(x, y), x, 2, y, 1)
assert g == fxy.diff(x, x, y)
assert g == fxy.diff(y, 1, x, 2)
Expand Down
6 changes: 5 additions & 1 deletion symengine/tests/test_number.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from symengine.test_utilities import raises

from symengine import Integer, I, S, Symbol, pi, Rational
from symengine import Integer, I, S, Symbol, pi, Rational, has_basic
from symengine.lib.symengine_wrapper import (perfect_power, is_square, integer_nthroot)


Expand Down Expand Up @@ -100,6 +100,10 @@ def test_is_conditions():
assert pi.is_Atom


def test_has_basic():
assert has_basic(3 + pi, pi)


def test_perfect_power():
assert perfect_power(1) == True
assert perfect_power(7) == False
Expand Down
2 changes: 1 addition & 1 deletion symengine_version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c9510fb4b5c30b84adb993573a51f2a9a38a4cfe
10d3591214e3f5906106f35ed19b6428b3fd86a3
Loading