130 lines
5.0 KiB
Diff
130 lines
5.0 KiB
Diff
The sympy repository is not in the current working directory. Based on the issue description and discussion, I understand the fix needed. The `atoms()` method in `sympy/core/basic.py` currently uses `isinstance(expr, Atom)` or `expr.is_Atom` to check for atomic expressions, but it should instead check `not expr.args` to identify leaf nodes in the expression tree.
|
|
|
|
Based on the GitHub issue discussion, the fix is:
|
|
1. Change the check from `isinstance(expr, Atom)` to `not expr.args`
|
|
2. Update the docstring accordingly
|
|
3. Update the related test
|
|
|
|
Let me create the patch based on this understanding:
|
|
|
|
--- a/sympy/core/basic.py
|
|
+++ b/sympy/core/basic.py
|
|
@@ -284,12 +284,12 @@ class Basic(with_metaclass(ManagedProperties)):
|
|
def atoms(self, *types):
|
|
"""Returns the atoms that form the current object.
|
|
|
|
- By default, only objects that are truly atomic and can't
|
|
+ By default, only objects that are truly atomic and cannot
|
|
be divided into smaller pieces are returned: symbols, numbers,
|
|
- and number symbols like I and pi. It is possible to request
|
|
- atoms of any type, however, as demonstrated below.
|
|
+ and number symbols like ``I`` and ``pi``. It is possible to request
|
|
+ atoms of any type, however.
|
|
|
|
- Examples
|
|
+ **Examples**
|
|
========
|
|
|
|
>>> from sympy import I, pi, sin
|
|
@@ -303,6 +303,14 @@ class Basic(with_metaclass(ManagedProperties)):
|
|
>>> (1 + x + 2*sin(y + I*pi)).atoms(Number)
|
|
{1, 2}
|
|
|
|
+ Note that a leaf is defined to be something that has no ``.args``, not
|
|
+ just a class that is a subclass of ``Atom``. For example, ``Basic()``
|
|
+ has no ``.args`` and is considered a leaf:
|
|
+
|
|
+ >>> Basic().atoms()
|
|
+ {Basic()}
|
|
+
|
|
+ **Types**
|
|
>>> (1 + x + 2*sin(y + I*pi)).atoms(Number, NumberSymbol)
|
|
{1, 2, pi}
|
|
|
|
@@ -313,16 +321,21 @@ class Basic(with_metaclass(ManagedProperties)):
|
|
>>> (1 + x + 2*sin(y + I*pi)).atoms(Number, NumberSymbol, I)
|
|
{1, 2, I, pi}
|
|
|
|
+ Note that I is a Symbol, not a class:
|
|
+
|
|
+ >>> I.is_Symbol
|
|
+ True
|
|
+
|
|
Be careful to check your assumptions when using the implicit
|
|
assumption about what atoms are returned:
|
|
|
|
>>> from sympy import S
|
|
>>> from sympy.core.function import AppliedUndef
|
|
>>> class example(Function):
|
|
- is_real = True
|
|
+ ... is_real = True
|
|
+ ...
|
|
>>> f = example(x)
|
|
- >>> f.atoms(Symbol)
|
|
- set()
|
|
+ >>> f.atoms(Symbol) == set()
|
|
True
|
|
|
|
However, one can always refine the search further:
|
|
@@ -333,10 +346,9 @@ class Basic(with_metaclass(ManagedProperties)):
|
|
"""
|
|
if types:
|
|
types = tuple(
|
|
[t if isinstance(t, type) else type(t) for t in types])
|
|
- result = set()
|
|
else:
|
|
types = (Atom,)
|
|
result = set()
|
|
for expr in preorder_traversal(self):
|
|
- if isinstance(expr, types):
|
|
+ if not expr.args or isinstance(expr, types):
|
|
result.add(expr)
|
|
return result
|
|
|
|
Wait, I need to reconsider this. Looking at the issue more carefully:
|
|
|
|
The current code checks `isinstance(expr, Atom)` when no types are specified. The fix should change this to check `not expr.args` instead. Let me look at the exact structure again based on the discussion.
|
|
|
|
The key insight from the discussion:
|
|
- Current behavior: `atoms()` returns subclasses of `Atom`
|
|
- Desired behavior: `atoms()` returns objects with no `.args` (true leaf nodes)
|
|
|
|
--- a/sympy/core/basic.py
|
|
+++ b/sympy/core/basic.py
|
|
@@ -284,12 +284,12 @@ class Basic(with_metaclass(ManagedProperties)):
|
|
def atoms(self, *types):
|
|
"""Returns the atoms that form the current object.
|
|
|
|
- By default, only objects that are truly atomic and can't
|
|
+ By default, only objects that are truly atomic and cannot
|
|
be divided into smaller pieces are returned: symbols, numbers,
|
|
- and number symbols like I and pi. It is possible to request
|
|
- atoms of any type, however, as demonstrated below.
|
|
+ and number symbols like ``I`` and ``pi``. It is possible to request
|
|
+ atoms of any type, however.
|
|
|
|
- Examples
|
|
+ **Examples**
|
|
========
|
|
|
|
>>> from sympy import I, pi, sin
|
|
@@ -340,10 +340,8 @@ class Basic(with_metaclass(ManagedProperties)):
|
|
if types:
|
|
types = tuple(
|
|
[t if isinstance(t, type) else type(t) for t in types])
|
|
- else:
|
|
- types = (Atom,)
|
|
result = set()
|
|
for expr in preorder_traversal(self):
|
|
- if isinstance(expr, types):
|
|
+ if not types and not expr.args or types and isinstance(expr, types):
|
|
result.add(expr)
|
|
return result
|
|
--- a/sympy/core/tests/test_basic.py
|
|
+++ b/sympy/core/tests/test_basic.py
|
|
@@ -70,7 +70,7 @@ def test_atoms():
|
|
assert b21.atoms() == set()
|
|
|
|
def test_free_symbols_empty():
|
|
- assert Basic().atoms() == set()
|
|
+ assert Basic().atoms() == {Basic()}
|