123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979 |
- from sympy.core import S, Basic, Dict, Symbol, Tuple, sympify
- from sympy.core.symbol import Str
- from sympy.sets import Set, FiniteSet, EmptySet
- from sympy.utilities.iterables import iterable
- class Class(Set):
- r"""
- The base class for any kind of class in the set-theoretic sense.
- Explanation
- ===========
- In axiomatic set theories, everything is a class. A class which
- can be a member of another class is a set. A class which is not a
- member of another class is a proper class. The class `\{1, 2\}`
- is a set; the class of all sets is a proper class.
- This class is essentially a synonym for :class:`sympy.core.Set`.
- The goal of this class is to assure easier migration to the
- eventual proper implementation of set theory.
- """
- is_proper = False
- class Object(Symbol):
- """
- The base class for any kind of object in an abstract category.
- Explanation
- ===========
- While technically any instance of :class:`~.Basic` will do, this
- class is the recommended way to create abstract objects in
- abstract categories.
- """
- class Morphism(Basic):
- """
- The base class for any morphism in an abstract category.
- Explanation
- ===========
- In abstract categories, a morphism is an arrow between two
- category objects. The object where the arrow starts is called the
- domain, while the object where the arrow ends is called the
- codomain.
- Two morphisms between the same pair of objects are considered to
- be the same morphisms. To distinguish between morphisms between
- the same objects use :class:`NamedMorphism`.
- It is prohibited to instantiate this class. Use one of the
- derived classes instead.
- See Also
- ========
- IdentityMorphism, NamedMorphism, CompositeMorphism
- """
- def __new__(cls, domain, codomain):
- raise(NotImplementedError(
- "Cannot instantiate Morphism. Use derived classes instead."))
- @property
- def domain(self):
- """
- Returns the domain of the morphism.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> f = NamedMorphism(A, B, "f")
- >>> f.domain
- Object("A")
- """
- return self.args[0]
- @property
- def codomain(self):
- """
- Returns the codomain of the morphism.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> f = NamedMorphism(A, B, "f")
- >>> f.codomain
- Object("B")
- """
- return self.args[1]
- def compose(self, other):
- r"""
- Composes self with the supplied morphism.
- The order of elements in the composition is the usual order,
- i.e., to construct `g\circ f` use ``g.compose(f)``.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> g * f
- CompositeMorphism((NamedMorphism(Object("A"), Object("B"), "f"),
- NamedMorphism(Object("B"), Object("C"), "g")))
- >>> (g * f).domain
- Object("A")
- >>> (g * f).codomain
- Object("C")
- """
- return CompositeMorphism(other, self)
- def __mul__(self, other):
- r"""
- Composes self with the supplied morphism.
- The semantics of this operation is given by the following
- equation: ``g * f == g.compose(f)`` for composable morphisms
- ``g`` and ``f``.
- See Also
- ========
- compose
- """
- return self.compose(other)
- class IdentityMorphism(Morphism):
- """
- Represents an identity morphism.
- Explanation
- ===========
- An identity morphism is a morphism with equal domain and codomain,
- which acts as an identity with respect to composition.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism, IdentityMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> f = NamedMorphism(A, B, "f")
- >>> id_A = IdentityMorphism(A)
- >>> id_B = IdentityMorphism(B)
- >>> f * id_A == f
- True
- >>> id_B * f == f
- True
- See Also
- ========
- Morphism
- """
- def __new__(cls, domain):
- return Basic.__new__(cls, domain)
- @property
- def codomain(self):
- return self.domain
- class NamedMorphism(Morphism):
- """
- Represents a morphism which has a name.
- Explanation
- ===========
- Names are used to distinguish between morphisms which have the
- same domain and codomain: two named morphisms are equal if they
- have the same domains, codomains, and names.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> f = NamedMorphism(A, B, "f")
- >>> f
- NamedMorphism(Object("A"), Object("B"), "f")
- >>> f.name
- 'f'
- See Also
- ========
- Morphism
- """
- def __new__(cls, domain, codomain, name):
- if not name:
- raise ValueError("Empty morphism names not allowed.")
- if not isinstance(name, Str):
- name = Str(name)
- return Basic.__new__(cls, domain, codomain, name)
- @property
- def name(self):
- """
- Returns the name of the morphism.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> f = NamedMorphism(A, B, "f")
- >>> f.name
- 'f'
- """
- return self.args[2].name
- class CompositeMorphism(Morphism):
- r"""
- Represents a morphism which is a composition of other morphisms.
- Explanation
- ===========
- Two composite morphisms are equal if the morphisms they were
- obtained from (components) are the same and were listed in the
- same order.
- The arguments to the constructor for this class should be listed
- in diagram order: to obtain the composition `g\circ f` from the
- instances of :class:`Morphism` ``g`` and ``f`` use
- ``CompositeMorphism(f, g)``.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism, CompositeMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> g * f
- CompositeMorphism((NamedMorphism(Object("A"), Object("B"), "f"),
- NamedMorphism(Object("B"), Object("C"), "g")))
- >>> CompositeMorphism(f, g) == g * f
- True
- """
- @staticmethod
- def _add_morphism(t, morphism):
- """
- Intelligently adds ``morphism`` to tuple ``t``.
- Explanation
- ===========
- If ``morphism`` is a composite morphism, its components are
- added to the tuple. If ``morphism`` is an identity, nothing
- is added to the tuple.
- No composability checks are performed.
- """
- if isinstance(morphism, CompositeMorphism):
- # ``morphism`` is a composite morphism; we have to
- # denest its components.
- return t + morphism.components
- elif isinstance(morphism, IdentityMorphism):
- # ``morphism`` is an identity. Nothing happens.
- return t
- else:
- return t + Tuple(morphism)
- def __new__(cls, *components):
- if components and not isinstance(components[0], Morphism):
- # Maybe the user has explicitly supplied a list of
- # morphisms.
- return CompositeMorphism.__new__(cls, *components[0])
- normalised_components = Tuple()
- for current, following in zip(components, components[1:]):
- if not isinstance(current, Morphism) or \
- not isinstance(following, Morphism):
- raise TypeError("All components must be morphisms.")
- if current.codomain != following.domain:
- raise ValueError("Uncomposable morphisms.")
- normalised_components = CompositeMorphism._add_morphism(
- normalised_components, current)
- # We haven't added the last morphism to the list of normalised
- # components. Add it now.
- normalised_components = CompositeMorphism._add_morphism(
- normalised_components, components[-1])
- if not normalised_components:
- # If ``normalised_components`` is empty, only identities
- # were supplied. Since they all were composable, they are
- # all the same identities.
- return components[0]
- elif len(normalised_components) == 1:
- # No sense to construct a whole CompositeMorphism.
- return normalised_components[0]
- return Basic.__new__(cls, normalised_components)
- @property
- def components(self):
- """
- Returns the components of this composite morphism.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> (g * f).components
- (NamedMorphism(Object("A"), Object("B"), "f"),
- NamedMorphism(Object("B"), Object("C"), "g"))
- """
- return self.args[0]
- @property
- def domain(self):
- """
- Returns the domain of this composite morphism.
- The domain of the composite morphism is the domain of its
- first component.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> (g * f).domain
- Object("A")
- """
- return self.components[0].domain
- @property
- def codomain(self):
- """
- Returns the codomain of this composite morphism.
- The codomain of the composite morphism is the codomain of its
- last component.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> (g * f).codomain
- Object("C")
- """
- return self.components[-1].codomain
- def flatten(self, new_name):
- """
- Forgets the composite structure of this morphism.
- Explanation
- ===========
- If ``new_name`` is not empty, returns a :class:`NamedMorphism`
- with the supplied name, otherwise returns a :class:`Morphism`.
- In both cases the domain of the new morphism is the domain of
- this composite morphism and the codomain of the new morphism
- is the codomain of this composite morphism.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> (g * f).flatten("h")
- NamedMorphism(Object("A"), Object("C"), "h")
- """
- return NamedMorphism(self.domain, self.codomain, new_name)
- class Category(Basic):
- r"""
- An (abstract) category.
- Explanation
- ===========
- A category [JoyOfCats] is a quadruple `\mbox{K} = (O, \hom, id,
- \circ)` consisting of
- * a (set-theoretical) class `O`, whose members are called
- `K`-objects,
- * for each pair `(A, B)` of `K`-objects, a set `\hom(A, B)` whose
- members are called `K`-morphisms from `A` to `B`,
- * for a each `K`-object `A`, a morphism `id:A\rightarrow A`,
- called the `K`-identity of `A`,
- * a composition law `\circ` associating with every `K`-morphisms
- `f:A\rightarrow B` and `g:B\rightarrow C` a `K`-morphism `g\circ
- f:A\rightarrow C`, called the composite of `f` and `g`.
- Composition is associative, `K`-identities are identities with
- respect to composition, and the sets `\hom(A, B)` are pairwise
- disjoint.
- This class knows nothing about its objects and morphisms.
- Concrete cases of (abstract) categories should be implemented as
- classes derived from this one.
- Certain instances of :class:`Diagram` can be asserted to be
- commutative in a :class:`Category` by supplying the argument
- ``commutative_diagrams`` in the constructor.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism, Diagram, Category
- >>> from sympy import FiniteSet
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> d = Diagram([f, g])
- >>> K = Category("K", commutative_diagrams=[d])
- >>> K.commutative_diagrams == FiniteSet(d)
- True
- See Also
- ========
- Diagram
- """
- def __new__(cls, name, objects=EmptySet, commutative_diagrams=EmptySet):
- if not name:
- raise ValueError("A Category cannot have an empty name.")
- if not isinstance(name, Str):
- name = Str(name)
- if not isinstance(objects, Class):
- objects = Class(objects)
- new_category = Basic.__new__(cls, name, objects,
- FiniteSet(*commutative_diagrams))
- return new_category
- @property
- def name(self):
- """
- Returns the name of this category.
- Examples
- ========
- >>> from sympy.categories import Category
- >>> K = Category("K")
- >>> K.name
- 'K'
- """
- return self.args[0].name
- @property
- def objects(self):
- """
- Returns the class of objects of this category.
- Examples
- ========
- >>> from sympy.categories import Object, Category
- >>> from sympy import FiniteSet
- >>> A = Object("A")
- >>> B = Object("B")
- >>> K = Category("K", FiniteSet(A, B))
- >>> K.objects
- Class({Object("A"), Object("B")})
- """
- return self.args[1]
- @property
- def commutative_diagrams(self):
- """
- Returns the :class:`~.FiniteSet` of diagrams which are known to
- be commutative in this category.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism, Diagram, Category
- >>> from sympy import FiniteSet
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> d = Diagram([f, g])
- >>> K = Category("K", commutative_diagrams=[d])
- >>> K.commutative_diagrams == FiniteSet(d)
- True
- """
- return self.args[2]
- def hom(self, A, B):
- raise NotImplementedError(
- "hom-sets are not implemented in Category.")
- def all_morphisms(self):
- raise NotImplementedError(
- "Obtaining the class of morphisms is not implemented in Category.")
- class Diagram(Basic):
- r"""
- Represents a diagram in a certain category.
- Explanation
- ===========
- Informally, a diagram is a collection of objects of a category and
- certain morphisms between them. A diagram is still a monoid with
- respect to morphism composition; i.e., identity morphisms, as well
- as all composites of morphisms included in the diagram belong to
- the diagram. For a more formal approach to this notion see
- [Pare1970].
- The components of composite morphisms are also added to the
- diagram. No properties are assigned to such morphisms by default.
- A commutative diagram is often accompanied by a statement of the
- following kind: "if such morphisms with such properties exist,
- then such morphisms which such properties exist and the diagram is
- commutative". To represent this, an instance of :class:`Diagram`
- includes a collection of morphisms which are the premises and
- another collection of conclusions. ``premises`` and
- ``conclusions`` associate morphisms belonging to the corresponding
- categories with the :class:`~.FiniteSet`'s of their properties.
- The set of properties of a composite morphism is the intersection
- of the sets of properties of its components. The domain and
- codomain of a conclusion morphism should be among the domains and
- codomains of the morphisms listed as the premises of a diagram.
- No checks are carried out of whether the supplied object and
- morphisms do belong to one and the same category.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism, Diagram
- >>> from sympy import pprint, default_sort_key
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> d = Diagram([f, g])
- >>> premises_keys = sorted(d.premises.keys(), key=default_sort_key)
- >>> pprint(premises_keys, use_unicode=False)
- [g*f:A-->C, id:A-->A, id:B-->B, id:C-->C, f:A-->B, g:B-->C]
- >>> pprint(d.premises, use_unicode=False)
- {g*f:A-->C: EmptySet, id:A-->A: EmptySet, id:B-->B: EmptySet, id:C-->C: EmptyS
- et, f:A-->B: EmptySet, g:B-->C: EmptySet}
- >>> d = Diagram([f, g], {g * f: "unique"})
- >>> pprint(d.conclusions,use_unicode=False)
- {g*f:A-->C: {unique}}
- References
- ==========
- [Pare1970] B. Pareigis: Categories and functors. Academic Press, 1970.
- """
- @staticmethod
- def _set_dict_union(dictionary, key, value):
- """
- If ``key`` is in ``dictionary``, set the new value of ``key``
- to be the union between the old value and ``value``.
- Otherwise, set the value of ``key`` to ``value.
- Returns ``True`` if the key already was in the dictionary and
- ``False`` otherwise.
- """
- if key in dictionary:
- dictionary[key] = dictionary[key] | value
- return True
- else:
- dictionary[key] = value
- return False
- @staticmethod
- def _add_morphism_closure(morphisms, morphism, props, add_identities=True,
- recurse_composites=True):
- """
- Adds a morphism and its attributes to the supplied dictionary
- ``morphisms``. If ``add_identities`` is True, also adds the
- identity morphisms for the domain and the codomain of
- ``morphism``.
- """
- if not Diagram._set_dict_union(morphisms, morphism, props):
- # We have just added a new morphism.
- if isinstance(morphism, IdentityMorphism):
- if props:
- # Properties for identity morphisms don't really
- # make sense, because very much is known about
- # identity morphisms already, so much that they
- # are trivial. Having properties for identity
- # morphisms would only be confusing.
- raise ValueError(
- "Instances of IdentityMorphism cannot have properties.")
- return
- if add_identities:
- empty = EmptySet
- id_dom = IdentityMorphism(morphism.domain)
- id_cod = IdentityMorphism(morphism.codomain)
- Diagram._set_dict_union(morphisms, id_dom, empty)
- Diagram._set_dict_union(morphisms, id_cod, empty)
- for existing_morphism, existing_props in list(morphisms.items()):
- new_props = existing_props & props
- if morphism.domain == existing_morphism.codomain:
- left = morphism * existing_morphism
- Diagram._set_dict_union(morphisms, left, new_props)
- if morphism.codomain == existing_morphism.domain:
- right = existing_morphism * morphism
- Diagram._set_dict_union(morphisms, right, new_props)
- if isinstance(morphism, CompositeMorphism) and recurse_composites:
- # This is a composite morphism, add its components as
- # well.
- empty = EmptySet
- for component in morphism.components:
- Diagram._add_morphism_closure(morphisms, component, empty,
- add_identities)
- def __new__(cls, *args):
- """
- Construct a new instance of Diagram.
- Explanation
- ===========
- If no arguments are supplied, an empty diagram is created.
- If at least an argument is supplied, ``args[0]`` is
- interpreted as the premises of the diagram. If ``args[0]`` is
- a list, it is interpreted as a list of :class:`Morphism`'s, in
- which each :class:`Morphism` has an empty set of properties.
- If ``args[0]`` is a Python dictionary or a :class:`Dict`, it
- is interpreted as a dictionary associating to some
- :class:`Morphism`'s some properties.
- If at least two arguments are supplied ``args[1]`` is
- interpreted as the conclusions of the diagram. The type of
- ``args[1]`` is interpreted in exactly the same way as the type
- of ``args[0]``. If only one argument is supplied, the diagram
- has no conclusions.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> from sympy.categories import IdentityMorphism, Diagram
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> d = Diagram([f, g])
- >>> IdentityMorphism(A) in d.premises.keys()
- True
- >>> g * f in d.premises.keys()
- True
- >>> d = Diagram([f, g], {g * f: "unique"})
- >>> d.conclusions[g * f]
- {unique}
- """
- premises = {}
- conclusions = {}
- # Here we will keep track of the objects which appear in the
- # premises.
- objects = EmptySet
- if len(args) >= 1:
- # We've got some premises in the arguments.
- premises_arg = args[0]
- if isinstance(premises_arg, list):
- # The user has supplied a list of morphisms, none of
- # which have any attributes.
- empty = EmptySet
- for morphism in premises_arg:
- objects |= FiniteSet(morphism.domain, morphism.codomain)
- Diagram._add_morphism_closure(premises, morphism, empty)
- elif isinstance(premises_arg, (dict, Dict)):
- # The user has supplied a dictionary of morphisms and
- # their properties.
- for morphism, props in premises_arg.items():
- objects |= FiniteSet(morphism.domain, morphism.codomain)
- Diagram._add_morphism_closure(
- premises, morphism, FiniteSet(*props) if iterable(props) else FiniteSet(props))
- if len(args) >= 2:
- # We also have some conclusions.
- conclusions_arg = args[1]
- if isinstance(conclusions_arg, list):
- # The user has supplied a list of morphisms, none of
- # which have any attributes.
- empty = EmptySet
- for morphism in conclusions_arg:
- # Check that no new objects appear in conclusions.
- if ((sympify(objects.contains(morphism.domain)) is S.true) and
- (sympify(objects.contains(morphism.codomain)) is S.true)):
- # No need to add identities and recurse
- # composites this time.
- Diagram._add_morphism_closure(
- conclusions, morphism, empty, add_identities=False,
- recurse_composites=False)
- elif isinstance(conclusions_arg, dict) or \
- isinstance(conclusions_arg, Dict):
- # The user has supplied a dictionary of morphisms and
- # their properties.
- for morphism, props in conclusions_arg.items():
- # Check that no new objects appear in conclusions.
- if (morphism.domain in objects) and \
- (morphism.codomain in objects):
- # No need to add identities and recurse
- # composites this time.
- Diagram._add_morphism_closure(
- conclusions, morphism, FiniteSet(*props) if iterable(props) else FiniteSet(props),
- add_identities=False, recurse_composites=False)
- return Basic.__new__(cls, Dict(premises), Dict(conclusions), objects)
- @property
- def premises(self):
- """
- Returns the premises of this diagram.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> from sympy.categories import IdentityMorphism, Diagram
- >>> from sympy import pretty
- >>> A = Object("A")
- >>> B = Object("B")
- >>> f = NamedMorphism(A, B, "f")
- >>> id_A = IdentityMorphism(A)
- >>> id_B = IdentityMorphism(B)
- >>> d = Diagram([f])
- >>> print(pretty(d.premises, use_unicode=False))
- {id:A-->A: EmptySet, id:B-->B: EmptySet, f:A-->B: EmptySet}
- """
- return self.args[0]
- @property
- def conclusions(self):
- """
- Returns the conclusions of this diagram.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism
- >>> from sympy.categories import IdentityMorphism, Diagram
- >>> from sympy import FiniteSet
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> d = Diagram([f, g])
- >>> IdentityMorphism(A) in d.premises.keys()
- True
- >>> g * f in d.premises.keys()
- True
- >>> d = Diagram([f, g], {g * f: "unique"})
- >>> d.conclusions[g * f] == FiniteSet("unique")
- True
- """
- return self.args[1]
- @property
- def objects(self):
- """
- Returns the :class:`~.FiniteSet` of objects that appear in this
- diagram.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism, Diagram
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> d = Diagram([f, g])
- >>> d.objects
- {Object("A"), Object("B"), Object("C")}
- """
- return self.args[2]
- def hom(self, A, B):
- """
- Returns a 2-tuple of sets of morphisms between objects ``A`` and
- ``B``: one set of morphisms listed as premises, and the other set
- of morphisms listed as conclusions.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism, Diagram
- >>> from sympy import pretty
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> d = Diagram([f, g], {g * f: "unique"})
- >>> print(pretty(d.hom(A, C), use_unicode=False))
- ({g*f:A-->C}, {g*f:A-->C})
- See Also
- ========
- Object, Morphism
- """
- premises = EmptySet
- conclusions = EmptySet
- for morphism in self.premises.keys():
- if (morphism.domain == A) and (morphism.codomain == B):
- premises |= FiniteSet(morphism)
- for morphism in self.conclusions.keys():
- if (morphism.domain == A) and (morphism.codomain == B):
- conclusions |= FiniteSet(morphism)
- return (premises, conclusions)
- def is_subdiagram(self, diagram):
- """
- Checks whether ``diagram`` is a subdiagram of ``self``.
- Diagram `D'` is a subdiagram of `D` if all premises
- (conclusions) of `D'` are contained in the premises
- (conclusions) of `D`. The morphisms contained
- both in `D'` and `D` should have the same properties for `D'`
- to be a subdiagram of `D`.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism, Diagram
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> d = Diagram([f, g], {g * f: "unique"})
- >>> d1 = Diagram([f])
- >>> d.is_subdiagram(d1)
- True
- >>> d1.is_subdiagram(d)
- False
- """
- premises = all((m in self.premises) and
- (diagram.premises[m] == self.premises[m])
- for m in diagram.premises)
- if not premises:
- return False
- conclusions = all((m in self.conclusions) and
- (diagram.conclusions[m] == self.conclusions[m])
- for m in diagram.conclusions)
- # Premises is surely ``True`` here.
- return conclusions
- def subdiagram_from_objects(self, objects):
- """
- If ``objects`` is a subset of the objects of ``self``, returns
- a diagram which has as premises all those premises of ``self``
- which have a domains and codomains in ``objects``, likewise
- for conclusions. Properties are preserved.
- Examples
- ========
- >>> from sympy.categories import Object, NamedMorphism, Diagram
- >>> from sympy import FiniteSet
- >>> A = Object("A")
- >>> B = Object("B")
- >>> C = Object("C")
- >>> f = NamedMorphism(A, B, "f")
- >>> g = NamedMorphism(B, C, "g")
- >>> d = Diagram([f, g], {f: "unique", g*f: "veryunique"})
- >>> d1 = d.subdiagram_from_objects(FiniteSet(A, B))
- >>> d1 == Diagram([f], {f: "unique"})
- True
- """
- if not objects.is_subset(self.objects):
- raise ValueError(
- "Supplied objects should all belong to the diagram.")
- new_premises = {}
- for morphism, props in self.premises.items():
- if ((sympify(objects.contains(morphism.domain)) is S.true) and
- (sympify(objects.contains(morphism.codomain)) is S.true)):
- new_premises[morphism] = props
- new_conclusions = {}
- for morphism, props in self.conclusions.items():
- if ((sympify(objects.contains(morphism.domain)) is S.true) and
- (sympify(objects.contains(morphism.codomain)) is S.true)):
- new_conclusions[morphism] = props
- return Diagram(new_premises, new_conclusions)
|