Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/geometry/hasse_diagram.py
4084 views
1
"""
2
Hasse diagrams of finite atomic and coatomic lattices.
3
4
This module provides the function
5
:func:`Hasse_diagram_from_incidences` for computing Hasse diagrams of
6
finite atomic and coatomic lattices in the sense of partially ordered
7
sets where any two elements have meet and joint. For example, the face
8
lattice of a polyhedron.
9
"""
10
11
#*****************************************************************************
12
# Copyright (C) 2010 Andrey Novoseltsev <[email protected]>
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
#
16
# http://www.gnu.org/licenses/
17
#*****************************************************************************
18
19
20
21
22
23
24
def Hasse_diagram_from_incidences(atom_to_coatoms, coatom_to_atoms,
25
face_constructor=None,
26
required_atoms=None,
27
key = None,
28
**kwds):
29
r"""
30
Compute the Hasse diagram of an atomic and coatomic lattice.
31
32
INPUT:
33
34
- ``atom_to_coatoms`` -- list, ``atom_to_coatom[i]`` should list all
35
coatoms over the ``i``-th atom;
36
37
- ``coatom_to_atoms`` -- list, ``coatom_to_atom[i]`` should list all
38
atoms under the ``i``-th coatom;
39
40
- ``face_constructor`` -- function or class taking as the first two
41
arguments sorted :class:`tuple` of integers and any keyword arguments.
42
It will be called to construct a face over atoms passed as the first
43
argument and under coatoms passed as the second argument. Default
44
implementation will just return these two tuples as a tuple;
45
46
- ``required_atoms`` -- list of atoms (default:None). Each
47
non-empty "face" requires at least on of the specified atoms
48
present. Used to ensure that each face has a vertex.
49
50
- ``key`` -- any hashable value (default: None). It is passed down
51
to :class:`~sage.combinat.posets.posets.FinitePoset`.
52
53
- all other keyword arguments will be passed to ``face_constructor`` on
54
each call.
55
56
OUTPUT:
57
58
- :class:`finite poset <sage.combinat.posets.posets.FinitePoset>` with
59
elements constructed by ``face_constructor``.
60
61
.. NOTE::
62
63
In addition to the specified partial order, finite posets in Sage have
64
internal total linear order of elements which extends the partial one.
65
This function will try to make this internal order to start with the
66
bottom and atoms in the order corresponding to ``atom_to_coatoms`` and
67
to finish with coatoms in the order corresponding to
68
``coatom_to_atoms`` and the top. This may not be possible if atoms and
69
coatoms are the same, in which case the preference is given to the
70
first list.
71
72
ALGORITHM:
73
74
The detailed description of the used algorithm is given in [KP2002]_.
75
76
The code of this function follows the pseudo-code description in the
77
section 2.5 of the paper, although it is mostly based on frozen sets
78
instead of sorted lists - this makes the implementation easier and should
79
not cost a big performance penalty. (If one wants to make this function
80
faster, it should be probably written in Cython.)
81
82
While the title of the paper mentions only polytopes, the algorithm (and
83
the implementation provided here) is applicable to any atomic and coatomic
84
lattice if both incidences are given, see Section 3.4.
85
86
In particular, this function can be used for strictly convex cones and
87
complete fans.
88
89
REFERENCES:
90
91
.. [KP2002]
92
Volker Kaibel and Marc E. Pfetsch,
93
"Computing the Face Lattice of a Polytope from its Vertex-Facet
94
Incidences", Computational Geometry: Theory and Applications,
95
Volume 23, Issue 3 (November 2002), 281-290.
96
Available at http://portal.acm.org/citation.cfm?id=763203
97
and free of charge at http://arxiv.org/abs/math/0106043
98
99
AUTHORS:
100
101
- Andrey Novoseltsev (2010-05-13) with thanks to Marshall Hampton for the
102
reference.
103
104
EXAMPLES:
105
106
Let's construct the Hasse diagram of a lattice of subsets of {0, 1, 2}.
107
Our atoms are {0}, {1}, and {2}, while our coatoms are {0,1}, {0,2}, and
108
{1,2}. Then incidences are ::
109
110
sage: atom_to_coatoms = [(0,1), (0,2), (1,2)]
111
sage: coatom_to_atoms = [(0,1), (0,2), (1,2)]
112
113
and we can compute the Hasse diagram as ::
114
115
sage: L = sage.geometry.cone.Hasse_diagram_from_incidences(
116
... atom_to_coatoms, coatom_to_atoms)
117
sage: L
118
Finite poset containing 8 elements
119
sage: for level in L.level_sets(): print level
120
[((), (0, 1, 2))]
121
[((0,), (0, 1)), ((1,), (0, 2)), ((2,), (1, 2))]
122
[((0, 1), (0,)), ((0, 2), (1,)), ((1, 2), (2,))]
123
[((0, 1, 2), ())]
124
125
For more involved examples see the *source code* of
126
:meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.face_lattice` and
127
:meth:`sage.geometry.fan.RationalPolyhedralFan._compute_cone_lattice`.
128
"""
129
from sage.graphs.all import DiGraph
130
from sage.combinat.posets.posets import FinitePoset
131
def default_face_constructor(atoms, coatoms, **kwds):
132
return (atoms, coatoms)
133
if face_constructor is None:
134
face_constructor = default_face_constructor
135
atom_to_coatoms = [frozenset(atc) for atc in atom_to_coatoms]
136
A = frozenset(range(len(atom_to_coatoms))) # All atoms
137
coatom_to_atoms = [frozenset(cta) for cta in coatom_to_atoms]
138
C = frozenset(range(len(coatom_to_atoms))) # All coatoms
139
# Comments with numbers correspond to steps in Section 2.5 of the article
140
L = DiGraph(1) # 3: initialize L
141
faces = dict()
142
atoms = frozenset()
143
coatoms = C
144
faces[atoms, coatoms] = 0
145
next_index = 1
146
Q = [(atoms, coatoms)] # 4: initialize Q with the empty face
147
while Q: # 5
148
q_atoms, q_coatoms = Q.pop() # 6: remove some q from Q
149
q = faces[q_atoms, q_coatoms]
150
# 7: compute H = {closure(q+atom) : atom not in atoms of q}
151
H = dict()
152
candidates = set(A.difference(q_atoms))
153
for atom in candidates:
154
coatoms = q_coatoms.intersection(atom_to_coatoms[atom])
155
atoms = A
156
for coatom in coatoms:
157
atoms = atoms.intersection(coatom_to_atoms[coatom])
158
H[atom] = (atoms, coatoms)
159
# 8: compute the set G of minimal sets in H
160
minimals = set([])
161
while candidates:
162
candidate = candidates.pop()
163
atoms = H[candidate][0]
164
if atoms.isdisjoint(candidates) and atoms.isdisjoint(minimals):
165
minimals.add(candidate)
166
# Now G == {H[atom] : atom in minimals}
167
for atom in minimals: # 9: for g in G:
168
g_atoms, g_coatoms = H[atom]
169
if not required_atoms is None:
170
if g_atoms.isdisjoint(required_atoms):
171
continue
172
if (g_atoms, g_coatoms) in faces:
173
g = faces[g_atoms, g_coatoms]
174
else: # 11: if g was newly created
175
g = next_index
176
faces[g_atoms, g_coatoms] = g
177
next_index += 1
178
Q.append((g_atoms, g_coatoms)) # 12
179
L.add_edge(q, g) # 14
180
# End of algorithm, now construct a FinitePoset.
181
# In principle, it is recommended to use Poset or in this case perhaps
182
# even LatticePoset, but it seems to take several times more time
183
# than the above computation, makes unnecessary copies, and crashes.
184
# So for now we will mimic the relevant code from Poset.
185
186
# Enumeration of graph vertices must be a linear extension of the poset
187
new_order = L.topological_sort()
188
# Make sure that coatoms are in the end in proper order
189
tail = [faces[atoms, frozenset([coatom])]
190
for coatom, atoms in enumerate(coatom_to_atoms)]
191
tail.append(faces[A, frozenset()])
192
new_order = [n for n in new_order if n not in tail] + tail
193
# Make sure that atoms are in the beginning in proper order
194
head = [0] # We know that the empty face has index 0
195
head.extend(faces[frozenset([atom]), coatoms]
196
for atom, coatoms in enumerate(atom_to_coatoms)
197
if required_atoms is None or atom in required_atoms)
198
new_order = head + [n for n in new_order if n not in head]
199
# "Invert" this list to a dictionary
200
labels = dict()
201
for new, old in enumerate(new_order):
202
labels[old] = new
203
L.relabel(labels)
204
# Construct the actual poset elements
205
elements = [None] * next_index
206
for face, index in faces.items():
207
atoms, coatoms = face
208
elements[labels[index]] = face_constructor(
209
tuple(sorted(atoms)), tuple(sorted(coatoms)), **kwds)
210
return FinitePoset(L, elements, key = key)
211
212
213
214