diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 36f4d1eee10..68ac9a62e2a 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -718,8 +718,8 @@ def Poset(data=None, element_labels=None, cover_relations=False, linear_extensio if len(data) == 2: # types 1 or 2 if callable(data[1]): # type 2 elements, function = data - relations = [[x, y] for x in elements for y in elements - if function(x, y)] + relations = ((x, y) for x in elements for y in elements + if function(x, y)) else: # type 1 elements, relations = data # check that relations are relations @@ -1079,10 +1079,10 @@ def __init__(self, hasse_diagram, elements, category, facade, key) -> None: for i in elements) # Relabel using the linear_extension. # So range(len(D)) becomes a linear extension of the poset. - rdict = {self._elements[i]: i for i in range(len(self._elements))} - self._hasse_diagram = HasseDiagram(hasse_diagram.relabel(rdict, inplace=False), data_structure="static_sparse") - self._element_to_vertex_dict = {self._elements[i]: i - for i in range(len(self._elements))} + rdict = {element: i for i, element in enumerate(self._elements)} + self._hasse_diagram = HasseDiagram(hasse_diagram.relabel(rdict, inplace=False), + data_structure="static_sparse") + self._element_to_vertex_dict = rdict self._is_facade = facade @lazy_attribute @@ -1110,8 +1110,8 @@ def _list(self): if self._is_facade: return self._elements else: - return tuple(self.element_class(self, self._elements[vertex], vertex) - for vertex in range(len(self._elements))) + return tuple(self.element_class(self, element, vertex) + for vertex, element in enumerate(self._elements)) # This defines the type (class) of elements of poset. Element = PosetElement @@ -2366,8 +2366,7 @@ def meet(self, x, y): mt = self._hasse_diagram._meet if mt[i, j] == -1: return None - else: - return self._vertex_to_element(mt[i, j]) + return self._vertex_to_element(mt[i, j]) def join(self, x, y): r""" @@ -2400,8 +2399,7 @@ def join(self, x, y): jn = self._hasse_diagram._join if jn[i, j] == -1: return None - else: - return self._vertex_to_element(jn[i, j]) + return self._vertex_to_element(jn[i, j]) def is_d_complete(self) -> bool: r""" @@ -2992,12 +2990,11 @@ def compare_elements(self, x, y): i, j = map(self._element_to_vertex, (x, y)) if i == j: return 0 - elif self._hasse_diagram.is_less_than(i, j): + if self._hasse_diagram.is_less_than(i, j): return -1 - elif self._hasse_diagram.is_less_than(j, i): + if self._hasse_diagram.is_less_than(j, i): return 1 - else: - return None + return None def minimal_elements(self): """ @@ -3049,8 +3046,7 @@ def bottom(self): hasse_bot = self._hasse_diagram.bottom() if hasse_bot is None: return None - else: - return self._vertex_to_element(hasse_bot) + return self._vertex_to_element(hasse_bot) def has_bottom(self): """ @@ -3105,8 +3101,7 @@ def top(self): hasse_top = self._hasse_diagram.top() if hasse_top is None: return None - else: - return self._vertex_to_element(hasse_top) + return self._vertex_to_element(hasse_top) def has_top(self): """ @@ -3185,7 +3180,7 @@ def height(self, certificate=False): max_chain.reverse() return (height, max_chain) - def has_isomorphic_subposet(self, other): + def has_isomorphic_subposet(self, other) -> bool: """ Return ``True`` if the poset contains a subposet isomorphic to ``other``. @@ -3210,13 +3205,10 @@ def has_isomorphic_subposet(self, other): sage: len([P for P in Posets(5) if P.has_isomorphic_subposet(D)]) 11 - """ if not hasattr(other, 'hasse_diagram'): raise TypeError("'other' is not a finite poset") - if self._hasse_diagram.transitive_closure().subgraph_search(other._hasse_diagram.transitive_closure(), induced=True) is None: - return False - return True + return self._hasse_diagram.transitive_closure().subgraph_search(other._hasse_diagram.transitive_closure(), induced=True) is not None def is_bounded(self) -> bool: """ @@ -3331,12 +3323,12 @@ def is_chain_of_poset(self, elms, ordered=False) -> bool: if ordered: sorted_o = elms return all(self.lt(a, b) for a, b in zip(sorted_o, sorted_o[1:])) - else: - # _element_to_vertex can be assumed to be a linear extension - # of the poset according to the documentation of class - # HasseDiagram. - sorted_o = sorted(elms, key=self._element_to_vertex) - return all(self.le(a, b) for a, b in zip(sorted_o, sorted_o[1:])) + + # _element_to_vertex can be assumed to be a linear extension + # of the poset according to the documentation of class + # HasseDiagram. + sorted_o = sorted(elms, key=self._element_to_vertex) + return all(self.le(a, b) for a, b in zip(sorted_o, sorted_o[1:])) def is_antichain_of_poset(self, elms): """ @@ -3494,14 +3486,14 @@ def is_EL_labelling(self, f, return_raising_chains=False): max_chains = sorted([[label_dict[(chain[i], chain[i + 1])] for i in range(len(chain) - 1)] for chain in P.maximal_chains_iterator()]) - if max_chains[0] != sorted(max_chains[0]) or any(max_chains[i] == sorted(max_chains[i]) for i in range(1, len(max_chains))): + if (max_chains[0] != sorted(max_chains[0]) or + any(max_chains[i] == sorted(max_chains[i]) for i in range(1, len(max_chains)))): return False - elif return_raising_chains: + if return_raising_chains: raising_chains[(a, b)] = max_chains[0] if return_raising_chains: return raising_chains - else: - return True + return True def dimension(self, certificate=False, *, solver=None, integrality_tolerance=1e-3): r""" @@ -3650,7 +3642,7 @@ def init_LP(k, cycles, inc_P): """ p = MixedIntegerLinearProgram(constraint_generation=True, solver=solver) b = p.new_variable(binary=True) - for (u, v) in inc_P: # Each point has a color + for u, v in inc_P: # Each point has a color p.add_constraint(p.sum(b[(u, v), i] for i in range(k)) == 1) p.add_constraint(p.sum(b[(v, u), i] for i in range(k)) == 1) for cycle in cycles: # No monochromatic set @@ -4632,8 +4624,7 @@ def is_isomorphic(self, other, **kwds): if hasattr(other, 'hasse_diagram'): return self.hasse_diagram().is_isomorphic(other.hasse_diagram(), **kwds) - else: - raise TypeError("'other' is not a finite poset") + raise TypeError("'other' is not a finite poset") def isomorphic_subposets_iterator(self, other): """ @@ -8310,7 +8301,15 @@ def frank_network(self): sage: ps = [[16,12,14,-13],[[12,14],[14,-13],[12,16],[16,-13]]] sage: G, e = Poset(ps).frank_network() sage: G.edges(sort=True) - [((-1, 0), (0, -13), None), ((-1, 0), (0, 12), None), ((-1, 0), (0, 14), None), ((-1, 0), (0, 16), None), ((0, -13), (1, -13), None), ((0, -13), (1, 12), None), ((0, -13), (1, 14), None), ((0, -13), (1, 16), None), ((0, 12), (1, 12), None), ((0, 14), (1, 12), None), ((0, 14), (1, 14), None), ((0, 16), (1, 12), None), ((0, 16), (1, 16), None), ((1, -13), (2, 0), None), ((1, 12), (2, 0), None), ((1, 14), (2, 0), None), ((1, 16), (2, 0), None)] + [((-1, 0), (0, -13), None), ((-1, 0), (0, 12), None), + ((-1, 0), (0, 14), None), ((-1, 0), (0, 16), None), + ((0, -13), (1, -13), None), ((0, -13), (1, 12), None), + ((0, -13), (1, 14), None), ((0, -13), (1, 16), None), + ((0, 12), (1, 12), None), ((0, 14), (1, 12), None), + ((0, 14), (1, 14), None), ((0, 16), (1, 12), None), + ((0, 16), (1, 16), None), ((1, -13), (2, 0), None), + ((1, 12), (2, 0), None), ((1, 14), (2, 0), None), + ((1, 16), (2, 0), None)] sage: e {((-1, 0), (0, -13)): 0, ((-1, 0), (0, 12)): 0, @@ -9078,14 +9077,16 @@ def _ford_fulkerson_chronicle(G, s, t, a): EXAMPLES:: sage: from sage.combinat.posets.posets import _ford_fulkerson_chronicle - sage: G = DiGraph({1: [3,6,7], 2: [4], 3: [7], 4: [], 6: [7,8], 7: [9], 8: [9,12], 9: [], 10: [], 12: []}) + sage: G = DiGraph({1: [3, 6, 7], 2: [4], 3: [7], 4: [], 6: [7, 8], + ....: 7: [9], 8: [9, 12], 9: [], 10: [], 12: []}) sage: s = 1 sage: t = 9 sage: (1, 6, None) in G.edges(sort=False) True sage: (1, 6) in G.edges(sort=False) False - sage: a = {(1, 6): 4, (2, 4): 0, (1, 3): 4, (1, 7): 1, (3, 7): 6, (7, 9): 1, (6, 7): 3, (6, 8): 1, (8, 9): 0, (8, 12): 2} + sage: a = {(1, 6): 4, (2, 4): 0, (1, 3): 4, (1, 7): 1, (3, 7): 6, + ....: (7, 9): 1, (6, 7): 3, (6, 8): 1, (8, 9): 0, (8, 12): 2} sage: ffc = _ford_fulkerson_chronicle(G, s, t, a) sage: next(ffc) (1, 0) @@ -9114,33 +9115,47 @@ def _ford_fulkerson_chronicle(G, s, t, a): sage: next(ffc) (11, 2) """ - # pi: potential function as a dictionary. - pi = {v: 0 for v in G} + n = G.order() + m = G.size() + + # Make a copy of the graph with vertices relabeled 0..n-1 + index_to_vertex = list(G) + vertex_to_index = {u: i for i, u in enumerate(index_to_vertex)} + G = G.relabel(perm=vertex_to_index, inplace=False) + s = vertex_to_index[s] + t = vertex_to_index[t] + # Associate each edge to an integer, its index + index_to_edge = list(G.edge_iterator(labels=False)) + edge_to_index = {e: i for i, e in enumerate(index_to_edge)} + # Change the cost function to a vector indexed by edge indices + a = [a[index_to_vertex[u], index_to_vertex[v]] for u, v in index_to_edge] + + # pi: potential function as a vector indexed by vertices. + pi = [0 for _ in range(n)] # p: value of the potential pi. p = 0 - # f: flow function as a dictionary. - f = {edge: 0 for edge in G.edge_iterator(labels=False)} + # f: flow function as a vector indexed by edge indices + f = [0 for _ in range(m)] # val: value of the flow f. (Cannot call it v due to Python's asinine # handling of for loops.) val = 0 - # capacity: capacity function as a dictionary. Here, just the - # indicator function of the set of arcs of G. - capacity = {edge: 1 for edge in G.edge_iterator(labels=False)} + # capacity: capacity function as a vector indexed by edge indices. Here, + # just the indicator function of the set of arcs of G. + capacity = [1 for _ in range(m)] while True: # Step MC1 in Britz-Fomin, Algorithm 7.2. # Gprime: directed graph G' from Britz-Fomin, Section 7. - Gprime = DiGraph() - Gprime.add_vertices(G) - for u, v in G.edge_iterator(labels=False): - if pi[v] - pi[u] == a[(u, v)]: - if f[(u, v)] < capacity[(u, v)]: + Gprime = DiGraph(n) + for e_index, (u, v) in enumerate(index_to_edge): + if pi[v] - pi[u] == a[e_index]: + if f[e_index] < capacity[e_index]: Gprime.add_edge(u, v) - elif f[(u, v)] > 0: + elif f[e_index] > 0: Gprime.add_edge(v, u) # X: list of vertices of G' reachable from s @@ -9150,14 +9165,14 @@ def _ford_fulkerson_chronicle(G, s, t, a): shortest_path = Gprime.shortest_path(s, t, by_weight=False) shortest_path_in_edges = zip(shortest_path[:-1], shortest_path[1:]) for u, v in shortest_path_in_edges: - if v in G.neighbor_out_iterator(u): - f[(u, v)] += 1 + if (u, v) in edge_to_index: + f[edge_to_index[u, v]] += 1 else: - f[(v, u)] -= 1 + f[edge_to_index[v, u]] -= 1 val += 1 else: # Step MC2b in Britz-Fomin, Algorithm 7.2. - for v in G: + for v in range(n): if v not in X: pi[v] += 1 p += 1