Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
src/sage/categories/families.py: New
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthias Koeppe committed Sep 17, 2022
1 parent 8b3bdef commit 5be1bdf
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 14 deletions.
156 changes: 156 additions & 0 deletions src/sage/categories/families.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
r"""
Families
"""

#*****************************************************************************
# Copyright (C) 2022 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 sage.misc.abstract_method import abstract_method
from sage.misc.cachefunc import cached_method
from sage.categories.category_singleton import Category_singleton
from sage.categories.sets_cat import Sets


class Families(Category_singleton):
r"""
The category of families
A *family* is a set together with a map from a set of "keys" onto it.
Morphisms of :class:`Families` preserve this map. This is the additional
structure compared to :class:`Sets`. Hence, equality of families takes
this map into account.
The standard methods for a family ``F`` are:
- ``F.keys()``, ``F.values()``, ``F.items()``: methods similar to those of
:class:`dict` (or :class:`collections.abc.Mapping`).
- ``F[key]``: the value (element) indexed by ``key``.
A family is not necessarily enumerated, and the map is not necessarily injective.
However, if it is iterable, then:
- ``iter(F)``: returns an iterator for the elements (values) of ``F`` as a set,
i.e., no value appears twice.
Stronger guarantees are given by the category :class:`EnumeratedFamilies`.
EXAMPLES::
sage: from sage.categories.families import Families
TESTS::
sage: C = Families()
sage: TestSuite(C).run()
"""

def super_categories(self):
r"""
EXAMPLES::
sage: from sage.categories.families import Families
sage: Families().super_categories()
[Category of sets]
"""
return [Sets()]

## def additional_structure(self):
## r"""
## Return ``self``.

## Indeed, the category of families defines an
## additional structure, namely the map from keys onto it,
## which shall be preserved by morphisms.

## .. SEEALSO:: :meth:`Category.additional_structure`

## EXAMPLES::

## sage: from sage.categories.families import Families
## sage: Families().additional_structure()
## Category of families
## """
## return self

class ParentMethods:

#def __getitem__(self, i):

@abstract_method
def keys(self):
"""
Return the keys of the family.
This may or may not be an iterable.
EXAMPLES::
sage: f = Family({3: 'a', 4: 'b', 7: 'd'})
sage: sorted(f.keys())
[3, 4, 7]
"""

@abstract_method(optional=True)
def values(self):
"""
Return the elements (values) of the family.
If :meth:`keys` returns an iterable, then :meth:`values` will
return an iterable parallel to that. When the family is not injective,
values will appear multiple times in the iteration.
EXAMPLES::
sage: f = Family(["c", "a", "b"], lambda x: x + x)
sage: sorted(f.values())
['aa', 'bb', 'cc']
"""

def items(self):
"""
Return the key-value pairs of the family.
This may or may not be an iterable.
A key can only appear once, but if the function is not injective, values will
appear multiple times.
EXAMPLES::
sage: f = Family(["a", "ab", "bc", "def"], len)
sage: sorted(f.items())
[('a', 1), ('ab', 2), ('bc', 2), ('def', 3)]
"""
return zip(self.keys(), self.values())

@cached_method
def as_set(self):
"""
Return the elements (values) of this family as a set.
EXAMPLES::
sage: f = Family({1: 'a', 2: 'b', 3: 'c'})
sage: g = Family({1: 'b', 2: 'c', 3: 'a'})
sage: f == g
False
sage: f.as_set() == g.as_set()
True
This is the same as calling :func:`~sage.sets.set.Set` on ``self``::
sage: f.as_set()
{...}
sage: Set(f)
{...}
"""
return Set(self.values())
32 changes: 18 additions & 14 deletions src/sage/sets/family.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
sage: P = Partitions(3)
sage: Family(P, lambda x: x).category()
Category of finite enumerated sets
Category of finite enumerated families
"""

#*****************************************************************************
Expand All @@ -45,6 +45,7 @@
from collections.abc import Iterable, Mapping, Sequence

from sage.categories.enumerated_sets import EnumeratedSets
from sage.categories.families import Families
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
from sage.categories.sets_cat import Sets
Expand Down Expand Up @@ -366,14 +367,14 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None,
sage: PA[(4, 10, 5, 0, 0)]
A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 4 vertices
sage: PA.category()
Category of sets
Category of families
We can refine the category::
sage: PA = Family(b_set, polyhedron_Ax_le_b, is_injective=False,
....: category=PolyhedralSets(RDF))
sage: PA.category()
Category of polyhedral sets over Real Double Field
Join of Category of polyhedral sets over Real Double Field and Category of families
The map is not injective::
Expand Down Expand Up @@ -568,9 +569,9 @@ def as_set(self):
This is the same as calling :func:`~sage.sets.set.Set`::
sage: f.as_set()
{'c', 'a', 'b'}
{...}
sage: Set(f)
{'c', 'a', 'b'}
{...}
"""
return Set(self.values())

Expand Down Expand Up @@ -731,7 +732,7 @@ def __init__(self, dictionary, keys=None, category=None):
Finite family {1: 'a', 3: 'b', 4: 'c'}
"""
# TODO: use keys to specify the order of the elements
super().__init__(category=FiniteEnumeratedSets().or_subcategory(category))
super().__init__(category=Families() & FiniteEnumeratedSets().or_subcategory(category))
self._dictionary = dict(dictionary)
self._keys = keys

Expand Down Expand Up @@ -1098,15 +1099,17 @@ def __init__(self, set, function, name=None, category=None, is_injective=None, i
Lazy family (<lambda>(i))_{i in [3, 4, 7]}
"""
if set in FiniteEnumeratedSets():
category = FiniteEnumeratedSets().or_subcategory(category)
set_category = FiniteEnumeratedSets()
elif set in InfiniteEnumeratedSets():
category = InfiniteEnumeratedSets().or_subcategory(category)
set_category = InfiniteEnumeratedSets()
elif isinstance(set, (list, tuple, range, CombinatorialClass)):
category = FiniteEnumeratedSets().or_subcategory(category)
set_category = FiniteEnumeratedSets()
elif set in Sets():
category = Sets().or_subcategory(category)
set_category = Sets()
elif category is None:
category = EnumeratedSets()
set_category = EnumeratedSets()

category = Families() & set_category.or_subcategory(category)

set = copy(set)

Expand All @@ -1129,6 +1132,7 @@ def __init__(self, set, function, name=None, category=None, is_injective=None, i
self.function_name = name
self._is_injective = is_injective
self._inverse = inverse
self._set_category = set_category

def __bool__(self):
r"""
Expand Down Expand Up @@ -1296,7 +1300,7 @@ def as_set(self):
"""
Return the set of values of ``self`` as an :class:`~sage.sets.image_set.ImageSet`.
"""
return ImageSubobject(self.function, self.set, category=self.category(),
return ImageSubobject(self.function, self.set, category=self._set_category,
is_injective=self._is_injective, inverse=self._inverse)

def cardinality(self):
Expand Down Expand Up @@ -1456,7 +1460,7 @@ def __init__(self, enumeration, category=None):
Family (3, 4, 7)
sage: TestSuite(f).run()
"""
category = FiniteEnumeratedSets().or_subcategory(category)
category = Families() & FiniteEnumeratedSets().or_subcategory(category)
super().__init__(category=category)
self._enumeration = tuple(enumeration)

Expand Down Expand Up @@ -1642,7 +1646,7 @@ def __init__(self, enumset, category=None):
sage: from sage.sets.family import EnumeratedFamily
sage: f = EnumeratedFamily(Permutations(4))
sage: f.category()
Category of finite enumerated sets
Category of finite enumerated families
sage: list(f.keys()) == list(range(f.cardinality()))
True
sage: Family(Permutations()).keys()
Expand Down

0 comments on commit 5be1bdf

Please sign in to comment.