Skip to content

Commit

Permalink
Trac #31938: Wrapper class for Sage sets as SymPy sets
Browse files Browse the repository at this point in the history
We add a `_sympy_` method to all sets. It creates an instance of the new
class `sage.interfaces.sympy_wrapper.SageSet`, which is a subclass of
!SymPy's [[https://docs.sympy.org/latest/modules/sets.html#set|Set]]
class.

Part of Meta-ticket #31926: Connect Sage sets to sympy sets

URL: https://trac.sagemath.org/31938
Reported by: mkoeppe
Ticket author(s): Matthias Koeppe
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Jun 29, 2021
2 parents 3a53bc3 + c06c965 commit c755280
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/sage/categories/sets_cat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,47 @@ def algebra(self, base_ring, category=None, **kwds):
result.__doc__ = Sets.ParentMethods.algebra.__doc__
return result

def _sympy_(self):
"""
Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``.
The default implementation creates an instance of
:class:`~sage.interfaces.sympy_wrapper`.
EXAMPLES::
sage: F = FiniteEnumeratedSets().example(); F
An example of a finite enumerated set: {1,2,3}
sage: sF = F._sympy_(); sF
SageSet(An example of a finite enumerated set: {1,2,3})
sage: sF.is_finite_set
True
sage: bool(sF)
True
sage: len(sF)
3
sage: list(sF)
[1, 2, 3]
sage: from sympy import FiniteSet
sage: FiniteSet.fromiter(sF)
FiniteSet(1, 2, 3)
sage: RR._sympy_().is_finite_set
False
sage: F = Set([1, 2])
sage: F is Set([1, 2])
False
sage: sF = F._sympy_(); sF
SageSet({1, 2})
sage: sF._sage_() is F
True
"""
from sage.interfaces.sympy_wrapper import SageSet
from sage.interfaces.sympy import sympy_init
sympy_init()
return SageSet(self)

class ElementMethods:
## Should eventually contain the basic operations which are no math
## latex, hash, ...
Expand Down
175 changes: 175 additions & 0 deletions src/sage/interfaces/sympy_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
"""
Wrapper Class for Sage Sets as SymPy Sets
"""

# ****************************************************************************
# Copyright (C) 2021 Matthias Koeppe
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************

from sympy.core.basic import Basic
from sympy.core.decorators import sympify_method_args
from sympy.core.sympify import sympify
from sympy.sets.sets import Set


@sympify_method_args
class SageSet(Set):
r"""
Wrapper for a Sage set providing the SymPy Set API.
Parents in the category :class:`sage.categories.sets_cat.Sets`, unless
a more specific method is implemented, convert to SymPy by creating
an instance of this class.
EXAMPLES::
sage: F = Family([2, 3, 5, 7]); F
Family (2, 3, 5, 7)
sage: sF = F._sympy_(); sF # indirect doctest
SageSet(Family (2, 3, 5, 7))
sage: sF._sage_() is F
True
sage: bool(sF)
True
sage: len(sF)
4
sage: list(sF)
[2, 3, 5, 7]
sage: sF.is_finite_set
True
"""

def __new__(cls, sage_set):
r"""
Construct a wrapper for a Sage set.
TESTS::
sage: from sage.interfaces.sympy_wrapper import SageSet
sage: F = Set([1, 2]); F
{1, 2}
sage: sF = SageSet(F); sF
SageSet({1, 2})
"""
return Basic.__new__(cls, sage_set)

def _sage_(self):
r"""
Return the underlying Sage set of the wrapper ``self``.
EXAMPLES::
sage: F = Set([1, 2])
sage: F is Set([1, 2])
False
sage: sF = F._sympy_(); sF
SageSet({1, 2})
sage: sF._sage_() is F
True
"""
return self._args[0]

@property
def is_empty(self):
r"""
Return whether the set ``self`` is empty.
EXAMPLES::
sage: Empty = Set([])
sage: sEmpty = Empty._sympy_()
sage: sEmpty.is_empty
True
"""
return self._sage_().is_empty()

@property
def is_finite_set(self):
r"""
Return whether the set ``self`` is finite.
EXAMPLES::
sage: W = WeylGroup(["A",1,1])
sage: sW = W._sympy_(); sW
SageSet(Weyl Group of type ['A', 1, 1] (as a matrix group acting on the root space))
sage: sW.is_finite_set
False
"""
return self._sage_().is_finite()

@property
def is_iterable(self):
r"""
Return whether the set ``self`` is iterable.
EXAMPLES::
sage: W = WeylGroup(["A",1,1])
sage: sW = W._sympy_(); sW
SageSet(Weyl Group of type ['A', 1, 1] (as a matrix group acting on the root space))
sage: sW.is_iterable
True
"""
from sage.categories.enumerated_sets import EnumeratedSets
return self._sage_() in EnumeratedSets()

def __iter__(self):
r"""
Iterator for the set ``self``.
EXAMPLES::
sage: sPrimes = Primes()._sympy_(); sPrimes
SageSet(Set of all prime numbers: 2, 3, 5, 7, ...)
sage: iter_sPrimes = iter(sPrimes)
sage: next(iter_sPrimes), next(iter_sPrimes), next(iter_sPrimes)
(2, 3, 5)
"""
for element in self._sage_():
yield sympify(element)

def _contains(self, element):
"""
Return whether ``element`` is an element of the set ``self``.
EXAMPLES::
sage: sPrimes = Primes()._sympy_(); sPrimes
SageSet(Set of all prime numbers: 2, 3, 5, 7, ...)
sage: 91 in sPrimes
False
sage: from sympy.abc import p
sage: sPrimes.contains(p)
Contains(p, SageSet(Set of all prime numbers: 2, 3, 5, 7, ...))
sage: p in sPrimes
Traceback (most recent call last):
...
TypeError: did not evaluate to a bool: None
"""
if element.is_symbol:
# keep symbolic
return None
return element in self._sage_()

def __len__(self):
"""
Return the cardinality of the finite set ``self``.
EXAMPLES::
sage: sB3 = WeylGroup(["B", 3])._sympy_(); sB3
SageSet(Weyl Group of type ['B', 3] (as a matrix group acting on the ambient space))
sage: len(sB3)
48
"""
return len(self._sage_())

0 comments on commit c755280

Please sign in to comment.