Skip to content

Commit 33182d9

Browse files
author
Release Manager
committed
gh-36225: improve the use of graphs in `sage/combinat/posets/*` The main changes are: - avoid sorting vertices or edges when not required - avoid asking for edge labels when not needed - use more iterators - avoid computing all paths from `s` in `_ford_fulkerson_chronicle` (in `posets.py`) ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> <!-- If your change requires a documentation PR, please link it appropriately --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> <!-- Feel free to remove irrelevant items. --> - [x] The title is concise, informative, and self-explanatory. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation accordingly. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on - #12345: short description why this is a dependency - #34567: ... --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> URL: #36225 Reported by: David Coudert Reviewer(s): Frédéric Chapoton
2 parents 33d6cc5 + d702234 commit 33182d9

File tree

5 files changed

+65
-64
lines changed

5 files changed

+65
-64
lines changed

src/sage/combinat/posets/d_complete.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,20 @@ def _hooks(self):
8989

9090
# Check if any of these make a longer double tailed diamond
9191
found_diamond = False
92-
for (mn, mx) in [(i, j) for i in potential_min for j in potential_max]:
93-
if len(H.neighbors_in(mx)) != 1:
92+
for mx in potential_max:
93+
if H.in_degree(mx) != 1:
9494
continue
95-
if len(H.all_paths(mn, mx)) == 2:
96-
# Success
97-
min_elmt = mn
98-
max_elmt = mx
99-
100-
min_diamond[mx] = mn
101-
max_diamond[mn] = mx
102-
diamond_index[mx] = index
103-
found_diamond = True
95+
for mn in potential_min:
96+
if len(H.all_paths(mn, mx)) == 2:
97+
# Success
98+
min_elmt = mn
99+
max_elmt = mx
100+
min_diamond[mx] = mn
101+
max_diamond[mn] = mx
102+
diamond_index[mx] = index
103+
found_diamond = True
104+
break
105+
if found_diamond:
104106
break
105107
if not found_diamond:
106108
break

src/sage/combinat/posets/hasse_diagram.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ def _precompute_intervals(self):
598598
"""
599599
n = self.order()
600600
v_up = (frozenset(self.depth_first_search(v)) for v in range(n))
601-
v_down = [frozenset(self.depth_first_search(v, neighbors=self.neighbors_in))
601+
v_down = [frozenset(self.depth_first_search(v, neighbors=self.neighbor_in_iterator))
602602
for v in range(n)]
603603
self._intervals = [[sorted(up.intersection(down)) for down in v_down]
604604
for up in v_up]
@@ -1232,7 +1232,7 @@ def order_ideal(self, elements):
12321232
[0, 1, 2, 3, 4, 5, 6, 7, 8, 10]
12331233
"""
12341234
return sorted(self.depth_first_search(elements,
1235-
neighbors=self.neighbors_in))
1235+
neighbors=self.neighbor_in_iterator))
12361236

12371237
def order_ideal_cardinality(self, elements):
12381238
r"""
@@ -1256,7 +1256,7 @@ def order_ideal_cardinality(self, elements):
12561256
continue
12571257
size += 1
12581258
seen.add(v)
1259-
q.extend(self.neighbors_in(v))
1259+
q.extend(self.neighbor_in_iterator(v))
12601260

12611261
return ZZ(size)
12621262

@@ -2545,9 +2545,9 @@ def common_upper_covers(self, vertices):
25452545
sage: H.common_upper_covers([4, 5]) # optional - sage.combinat
25462546
[6]
25472547
"""
2548-
covers = set(self.neighbors_out(vertices.pop()))
2548+
covers = set(self.neighbor_out_iterator(vertices.pop()))
25492549
for v in vertices:
2550-
covers = covers.intersection(self.neighbors_out(v))
2550+
covers = covers.intersection(self.neighbor_out_iterator(v))
25512551
return list(covers)
25522552

25532553
def common_lower_covers(self, vertices):
@@ -2566,9 +2566,9 @@ def common_lower_covers(self, vertices):
25662566
sage: H.common_lower_covers([4, 5]) # optional - sage.combinat
25672567
[3]
25682568
"""
2569-
covers = set(self.neighbors_in(vertices.pop()))
2569+
covers = set(self.neighbor_in_iterator(vertices.pop()))
25702570
for v in vertices:
2571-
covers = covers.intersection(self.neighbors_in(v))
2571+
covers = covers.intersection(self.neighbor_in_iterator(v))
25722572
return list(covers)
25732573

25742574
def _trivial_nonregular_congruence(self):
@@ -2764,7 +2764,7 @@ def sublattice(elms, e):
27642764
break
27652765

27662766
# Special case to handle at last.
2767-
if len(self.neighbors_out(0)) == 1:
2767+
if self.out_degree(0) == 1:
27682768
result.append(set(range(1, N)))
27692769

27702770
return result
@@ -2830,8 +2830,8 @@ def kappa_dual(self, a):
28302830
uc = next(self.neighbor_out_iterator(a))
28312831
if self.in_degree(uc) == 1:
28322832
return uc
2833-
lt_a = set(self.depth_first_search(a, neighbors=self.neighbors_in))
2834-
tmp = list(self.depth_first_search(uc, neighbors=lambda v: [v_ for v_ in self.neighbor_in_iterator(v) if v_ not in lt_a]))
2833+
lt_a = set(self.depth_first_search(a, neighbors=self.neighbor_in_iterator))
2834+
tmp = set(self.depth_first_search(uc, neighbors=lambda v: [v_ for v_ in self.neighbor_in_iterator(v) if v_ not in lt_a]))
28352835
result = None
28362836
for e in tmp:
28372837
if all(x not in tmp for x in self.neighbor_in_iterator(e)):
@@ -2990,7 +2990,7 @@ def neutral_elements(self):
29902990

29912991
def is_neutral(a):
29922992
noncomp = all_elements.difference(self.depth_first_search(a))
2993-
noncomp.difference_update(self.depth_first_search(a, neighbors=self.neighbors_in))
2993+
noncomp.difference_update(self.depth_first_search(a, neighbors=self.neighbor_in_iterator))
29942994

29952995
for x in noncomp.intersection(todo):
29962996
meet_ax = mt[a, x]
@@ -3071,7 +3071,7 @@ def kappa(self, a):
30713071
if self.out_degree(lc) == 1:
30723072
return lc
30733073
gt_a = set(self.depth_first_search(a))
3074-
tmp = list(self.depth_first_search(lc, neighbors=lambda v: [v_ for v_ in self.neighbor_out_iterator(v) if v_ not in gt_a]))
3074+
tmp = set(self.depth_first_search(lc, neighbors=lambda v: [v_ for v_ in self.neighbor_out_iterator(v) if v_ not in gt_a]))
30753075
result = None
30763076
for e in tmp:
30773077
if all(x not in tmp for x in self.neighbor_out_iterator(e)):
@@ -3346,9 +3346,9 @@ def find_nontrivial_congruence(self):
33463346
join_irreducibles = [v for v in self if self.in_degree(v) == 1]
33473347
meet_irreducibles = [v for v in self if self.out_degree(v) == 1]
33483348
if len(join_irreducibles) < len(meet_irreducibles):
3349-
irr = [(v, self.neighbors_in(v)[0]) for v in join_irreducibles]
3349+
irr = [(v, next(self.neighbor_in_iterator(v))) for v in join_irreducibles]
33503350
else:
3351-
irr = [(self.neighbors_out(v)[0], v) for v in meet_irreducibles]
3351+
irr = [(next(self.neighbor_out_iterator(v)), v) for v in meet_irreducibles]
33523352
irr.sort(key=lambda x: x[0] - x[1])
33533353
tried = []
33543354
for pair in irr:

src/sage/combinat/posets/lattices.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,7 +1695,7 @@ def is_cosectionally_complemented(self, certificate=False):
16951695
n = H.order()
16961696
for e in range(n - 2, -1, -1):
16971697
t = 0
1698-
for uc in H.neighbors_out(e):
1698+
for uc in H.neighbor_out_iterator(e):
16991699
t = jn[t, uc]
17001700
if t == n - 1:
17011701
break
@@ -1805,13 +1805,13 @@ def is_relatively_complemented(self, certificate=False):
18051805
return False
18061806

18071807
for e1 in range(n - 1):
1808-
C = Counter(flatten([H.neighbors_out(e2) for e2 in H.neighbors_out(e1)]))
1808+
C = Counter(flatten([H.neighbors_out(e2) for e2 in H.neighbor_out_iterator(e1)]))
18091809
for e3, c in C.items():
18101810
if c == 1 and len(H.closed_interval(e1, e3)) == 3:
18111811
if not certificate:
18121812
return False
1813-
for e2 in H.neighbors_in(e3):
1814-
if e2 in H.neighbors_out(e1):
1813+
for e2 in H.neighbor_in_iterator(e3):
1814+
if e2 in H.neighbor_out_iterator(e1):
18151815
break
18161816
return (False, (self._vertex_to_element(e1),
18171817
self._vertex_to_element(e2),
@@ -1885,7 +1885,7 @@ def is_sectionally_complemented(self, certificate=False):
18851885
n = H.order() - 1
18861886
for e in range(2, n + 1):
18871887
t = n
1888-
for lc in H.neighbors_in(e):
1888+
for lc in H.neighbor_in_iterator(e):
18891889
t = mt[t, lc]
18901890
if t == 0:
18911891
break
@@ -1988,7 +1988,7 @@ def join(L):
19881988

19891989
# Get elements more than B levels below it.
19901990
too_close = set(H.breadth_first_search(j,
1991-
neighbors=H.neighbors_in,
1991+
neighbors=H.neighbor_in_iterator,
19921992
distance=B - 2))
19931993
elems = [e for e in H.order_ideal([j]) if e not in too_close]
19941994

@@ -2378,7 +2378,7 @@ def is_atomic(self, certificate=False):
23782378
if self.cardinality() < 3:
23792379
return (True, None)
23802380
H = self._hasse_diagram
2381-
atoms = set(H.neighbors_out(0))
2381+
atoms = set(H.neighbor_out_iterator(0))
23822382
for v in H:
23832383
if H.in_degree(v) == 1 and v not in atoms:
23842384
return (False, self._vertex_to_element(v))
@@ -2436,7 +2436,7 @@ def is_coatomic(self, certificate=False):
24362436
if self.cardinality() < 3:
24372437
return (True, None)
24382438
H = self._hasse_diagram
2439-
coatoms = set(H.neighbors_in(n - 1))
2439+
coatoms = set(H.neighbor_in_iterator(n - 1))
24402440
for v in H:
24412441
if H.out_degree(v) == 1 and v not in coatoms:
24422442
return (False, self._vertex_to_element(v))
@@ -3782,8 +3782,8 @@ def is_dismantlable(self, certificate=False):
37823782
if certificate:
37833783
cert.append(e)
37843784
if i == 1 and o == 1: # Remove inside the lattice
3785-
lower = H.neighbors_in(e)[0]
3786-
upper = H.neighbors_out(e)[0]
3785+
lower = next(H.neighbor_in_iterator(e))
3786+
upper = next(H.neighbor_out_iterator(e))
37873787
H.delete_vertex(e)
37883788
if upper not in H.depth_first_search(lower):
37893789
H.add_edge(lower, upper)
@@ -4125,11 +4125,11 @@ def canonical_meetands(self, e):
41254125
H = self._hasse_diagram
41264126
e = self._element_to_vertex(e)
41274127
meetands = []
4128-
for a in H.neighbors_out(e):
4129-
above_a = list(H.depth_first_search(a))
4128+
for a in H.neighbor_out_iterator(e):
4129+
above_a = set(H.depth_first_search(a))
41304130

41314131
def go_up(v):
4132-
return [v_ for v_ in H.neighbors_out(v) if v_ not in above_a]
4132+
return [v_ for v_ in H.neighbor_out_iterator(v) if v_ not in above_a]
41334133

41344134
result = None
41354135
for v in H.depth_first_search(e, neighbors=go_up):
@@ -4191,11 +4191,11 @@ def canonical_joinands(self, e):
41914191
H = self._hasse_diagram
41924192
e = self._element_to_vertex(e)
41934193
joinands = []
4194-
for a in H.neighbors_in(e):
4195-
below_a = list(H.depth_first_search(a, neighbors=H.neighbors_in))
4194+
for a in H.neighbor_in_iterator(e):
4195+
below_a = set(H.depth_first_search(a, neighbors=H.neighbor_in_iterator))
41964196

41974197
def go_down(v):
4198-
return [v_ for v_ in H.neighbors_in(v) if v_ not in below_a]
4198+
return [v_ for v_ in H.neighbor_in_iterator(v) if v_ not in below_a]
41994199

42004200
result = None
42014201
for v in H.depth_first_search(e, neighbors=go_down):

src/sage/combinat/posets/mobile.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@ def _is_valid_ribbon(self, ribbon):
112112
num_anchors = 0
113113

114114
for r in ribbon:
115-
anchor_neighbors = set(G.neighbors_out(r)).difference(set(R.neighbors_out(r)))
115+
anchor_neighbors = set(G.neighbor_out_iterator(r)).difference(set(R.neighbor_out_iterator(r)))
116116
if len(anchor_neighbors) == 1:
117117
num_anchors += 1
118118
elif len(anchor_neighbors) > 1:
119119
return False
120120

121-
for lc in G.neighbors_in(r):
121+
for lc in G.neighbor_in_iterator(r):
122122
if lc in ribbon:
123123
continue
124124

@@ -151,7 +151,7 @@ def _anchor(self):
151151

152152
# Find the anchor vertex, if it exists, and return the edge
153153
for r in ribbon:
154-
anchor_neighbors = set(H.neighbors_out(r)).difference(set(R.neighbors_out(r)))
154+
anchor_neighbors = set(H.neighbor_out_iterator(r)).difference(set(R.neighbor_out_iterator(r)))
155155
if len(anchor_neighbors) == 1:
156156
anchor = (r, anchor_neighbors.pop())
157157
break

src/sage/combinat/posets/posets.py

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,7 +2065,7 @@ def plot(self, label_elements=True, element_labels=None,
20652065

20662066
if cover_labels is not None:
20672067
if callable(cover_labels):
2068-
for v, w in graph.edges(sort=True, labels=False):
2068+
for v, w in graph.edges(sort=False, labels=False):
20692069
graph.set_edge_label(v, w, cover_labels(v, w))
20702070
elif isinstance(cover_labels, dict):
20712071
for v, w in cover_labels:
@@ -2453,7 +2453,7 @@ def is_d_complete(self) -> bool:
24532453
min_elmt = d[0]
24542454
max_elmt = d[3]
24552455

2456-
if len(H.neighbors_in(max_elmt)) != 2:
2456+
if H.in_degree(max_elmt) != 2:
24572457
# Top of a diamond cannot cover anything but the two side elements
24582458
return False
24592459

@@ -2469,7 +2469,7 @@ def is_d_complete(self) -> bool:
24692469
continue
24702470
for mx in potential_max:
24712471
if len(H.all_paths(mn, mx)) == 2:
2472-
if len(H.neighbors_in(mx)) != 1:
2472+
if H.in_degree(mx) != 1:
24732473
# Max element covers something outside of double tailed diamond
24742474
return False
24752475
# Success
@@ -3632,7 +3632,7 @@ def dimension(self, certificate=False, *, solver=None, integrality_tolerance=1e-
36323632
P = Poset(self._hasse_diagram) # work on an int-labelled poset
36333633
hasse_diagram = P.hasse_diagram()
36343634
inc_graph = P.incomparability_graph()
3635-
inc_P = inc_graph.edges(sort=True, labels=False)
3635+
inc_P = inc_graph.edges(sort=False, labels=False)
36363636

36373637
# cycles is the list of all cycles found during the execution of the
36383638
# algorithm
@@ -5323,9 +5323,9 @@ def edge_color(va, vb):
53235323

53245324
for i0, i1 in Subsets(factors_range, 2):
53255325
for x in prod_dg:
5326-
neigh0 = [y for y in prod_dg.neighbors(x)
5326+
neigh0 = [y for y in prod_dg.neighbor_iterator(x)
53275327
if edge_color(x, y) == i0]
5328-
neigh1 = [z for z in prod_dg.neighbors(x)
5328+
neigh1 = [z for z in prod_dg.neighbor_iterator(x)
53295329
if edge_color(x, z) == i1]
53305330
for x0, x1 in cartesian_product_iterator([neigh0, neigh1]):
53315331
x2 = list(x0)
@@ -7175,7 +7175,7 @@ def order_polytope(self):
71757175
"""
71767176
from sage.geometry.polyhedron.constructor import Polyhedron
71777177
ineqs = [[0] + [ZZ(j == v) - ZZ(j == u) for j in self]
7178-
for u, v, w in self.hasse_diagram().edges(sort=True)]
7178+
for u, v in self.hasse_diagram().edges(sort=False, labels=False)]
71797179
for i in self.maximal_elements():
71807180
ineqs += [[1] + [-ZZ(j == i) for j in self]]
71817181
for i in self.minimal_elements():
@@ -8369,7 +8369,7 @@ def frank_network(self):
83698369
pdict[(0, i)] = [(1, j) for j in self if self.ge(i, j)]
83708370
pdict[(1, i)] = [(2, 0)]
83718371
G = DiGraph(pdict, format="dict_of_lists")
8372-
a = {(u, v): 0 for (u, v, l) in G.edge_iterator()}
8372+
a = {e: 0 for e in G.edge_iterator(labels=False)}
83738373
for i in self:
83748374
a[((0, i), (1, i))] = 1
83758375
return (G, a)
@@ -9108,50 +9108,49 @@ def _ford_fulkerson_chronicle(G, s, t, a):
91089108
(11, 2)
91099109
"""
91109110
# pi: potential function as a dictionary.
9111-
pi = {v: 0 for v in G.vertex_iterator()}
9111+
pi = {v: 0 for v in G}
91129112
# p: value of the potential pi.
91139113
p = 0
91149114

91159115
# f: flow function as a dictionary.
9116-
f = {(u, v): 0 for (u, v, l) in G.edge_iterator()}
9116+
f = {edge: 0 for edge in G.edge_iterator(labels=False)}
91179117
# val: value of the flow f. (Cannot call it v due to Python's asinine
91189118
# handling of for loops.)
91199119
val = 0
91209120

91219121
# capacity: capacity function as a dictionary. Here, just the
91229122
# indicator function of the set of arcs of G.
9123-
capacity = {(u, v): 1 for (u, v, l) in G.edge_iterator()}
9123+
capacity = {edge: 1 for edge in G.edge_iterator(labels=False)}
91249124

91259125
while True:
91269126

91279127
# Step MC1 in Britz-Fomin, Algorithm 7.2.
91289128

91299129
# Gprime: directed graph G' from Britz-Fomin, Section 7.
91309130
Gprime = DiGraph()
9131-
Gprime.add_vertices(G.vertices(sort=False))
9132-
for (u, v, l) in G.edge_iterator():
9131+
Gprime.add_vertices(G)
9132+
for u, v in G.edge_iterator(labels=False):
91339133
if pi[v] - pi[u] == a[(u, v)]:
91349134
if f[(u, v)] < capacity[(u, v)]:
91359135
Gprime.add_edge(u, v)
91369136
elif f[(u, v)] > 0:
91379137
Gprime.add_edge(v, u)
91389138

9139-
# X: list of vertices of G' reachable from s, along with
9140-
# the shortest paths from s to them.
9141-
X = Gprime.shortest_paths(s)
9139+
# X: list of vertices of G' reachable from s
9140+
X = set(Gprime.depth_first_search(s))
91429141
if t in X:
91439142
# Step MC2a in Britz-Fomin, Algorithm 7.2.
9144-
shortest_path = X[t]
9143+
shortest_path = Gprime.shortest_path(s, t, by_weight=False)
91459144
shortest_path_in_edges = zip(shortest_path[:-1], shortest_path[1:])
9146-
for (u, v) in shortest_path_in_edges:
9147-
if v in G.neighbors_out(u):
9145+
for u, v in shortest_path_in_edges:
9146+
if v in G.neighbor_out_iterator(u):
91489147
f[(u, v)] += 1
91499148
else:
91509149
f[(v, u)] -= 1
91519150
val += 1
91529151
else:
91539152
# Step MC2b in Britz-Fomin, Algorithm 7.2.
9154-
for v in G.vertex_iterator():
9153+
for v in G:
91559154
if v not in X:
91569155
pi[v] += 1
91579156
p += 1

0 commit comments

Comments
 (0)