Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve method _ford fulkerson chronicle in src/sage/combinat/posets/posets.py #37571

Merged
merged 2 commits into from
Mar 31, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 73 additions & 58 deletions src/sage/combinat/posets/posets.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,8 +718,8 @@
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
Expand Down Expand Up @@ -1079,10 +1079,10 @@
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
Expand Down Expand Up @@ -1110,8 +1110,8 @@
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
Expand Down Expand Up @@ -2366,8 +2366,7 @@
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"""
Expand Down Expand Up @@ -2400,8 +2399,7 @@
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"""
Expand Down Expand Up @@ -2992,12 +2990,11 @@
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):
"""
Expand Down Expand Up @@ -3049,8 +3046,7 @@
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):
"""
Expand Down Expand Up @@ -3105,8 +3101,7 @@
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):
"""
Expand Down Expand Up @@ -3185,7 +3180,7 @@
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``.
Expand All @@ -3210,13 +3205,10 @@

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:
"""
Expand Down Expand Up @@ -3331,12 +3323,12 @@
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):
"""
Expand Down Expand Up @@ -3494,14 +3486,14 @@
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"""
Expand Down Expand Up @@ -3650,7 +3642,7 @@
"""
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
Expand Down Expand Up @@ -4632,8 +4624,7 @@
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")

Check warning on line 4627 in src/sage/combinat/posets/posets.py

View check run for this annotation

Codecov / codecov/patch

src/sage/combinat/posets/posets.py#L4627

Added line #L4627 was not covered by tests

def isomorphic_subposets_iterator(self, other):
"""
Expand Down Expand Up @@ -8310,7 +8301,15 @@
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,
Expand Down Expand Up @@ -9078,14 +9077,16 @@
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)
Expand Down Expand Up @@ -9114,33 +9115,47 @@
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
Expand All @@ -9150,14 +9165,14 @@
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
Expand Down
Loading