Skip to content

Commit

Permalink
Asadpour output for ascent method
Browse files Browse the repository at this point in the history
  • Loading branch information
mjschwenne committed Jul 1, 2021
1 parent 2fcc1fe commit 37d6219
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 61 deletions.
95 changes: 78 additions & 17 deletions networkx/algorithms/approximation/tests/test_traveling_salesman.py
Expand Up @@ -400,21 +400,46 @@ def test_held_karp_ascent():
]
)

solution_edges = [
(1, 5, 30),
(2, 3, 21),
(3, 1, 52),
(4, 2, 35),
(5, 0, 52),
(0, 4, 17),
]
solution = nx.DiGraph()
solution.add_weighted_edges_from(solution_edges)
solution_z_star = {
(0, 1): 0.0,
(0, 2): 0.0,
(0, 3): 0.0,
(0, 4): 5 / 6,
(0, 5): 5 / 6,
(1, 0): 0.0,
(1, 2): 0.0,
(1, 3): 5 / 6,
(1, 4): 0.0,
(1, 5): 5 / 6,
(2, 0): 0.0,
(2, 1): 0.0,
(2, 3): 5 / 6,
(2, 4): 5 / 6,
(2, 5): 0.0,
(3, 0): 0.0,
(3, 1): 5 / 6,
(3, 2): 5 / 6,
(3, 4): 0.0,
(3, 5): 0.0,
(4, 0): 5 / 6,
(4, 1): 0.0,
(4, 2): 5 / 6,
(4, 3): 0.0,
(4, 5): 0.0,
(5, 0): 5 / 6,
(5, 1): 5 / 6,
(5, 2): 0.0,
(5, 3): 0.0,
(5, 4): 0.0,
}

G = nx.from_numpy_array(G_array, create_using=nx.DiGraph)
k = tsp.held_karp_ascent(G)
opt_hk, z_star = tsp.held_karp_ascent(G)

assert nx.utils.edges_equal(k.edges(), solution.edges())
# Check that the optimal weights are the same
assert opt_hk == 207
# Check that the z_stars are the same
assert z_star == solution_z_star


def test_held_karp_branch_bound():
Expand Down Expand Up @@ -476,12 +501,48 @@ def test_ascent_fractional_solution():
]
)

G = nx.from_numpy_array(G_array, create_using=nx.DiGraph)
k = tsp.held_karp_ascent(G)
solution_z_star = {
(0, 1): 5 / 12,
(0, 2): 5 / 12,
(0, 3): 0.0,
(0, 4): 0.0,
(0, 5): 5 / 6,
(1, 0): 5 / 12,
(1, 2): 1 / 3,
(1, 3): 0.0,
(1, 4): 5 / 6,
(1, 5): 0.0,
(2, 0): 5 / 12,
(2, 1): 1 / 3,
(2, 3): 5 / 6,
(2, 4): 0.0,
(2, 5): 0.0,
(3, 0): 0.0,
(3, 1): 0.0,
(3, 2): 5 / 6,
(3, 4): 1 / 3,
(3, 5): 1 / 2,
(4, 0): 0.0,
(4, 1): 5 / 6,
(4, 2): 0.0,
(4, 3): 1 / 3,
(4, 5): 1 / 2,
(5, 0): 5 / 6,
(5, 1): 0.0,
(5, 2): 0.0,
(5, 3): 1 / 2,
(5, 4): 1 / 2,
}

print()
for u, v, d in k.edges(data=True):
print(f"({u}, {v}, {d['weight']})")
G = nx.from_numpy_array(G_array, create_using=nx.DiGraph)
opt_hk, z_star = tsp.held_karp_ascent(G)

# Check that the optimal weights are the same
assert opt_hk == 303
# Check that the z_stars are the same
assert {key: round(z_star[key], 4) for key in z_star} == {
key: round(solution_z_star[key], 4) for key in solution_z_star
}


def test_branch_bound_fractional_solution():
Expand Down
147 changes: 103 additions & 44 deletions networkx/algorithms/approximation/traveling_salesman.py
Expand Up @@ -449,24 +449,24 @@ def k_pi():
# plus the difference between the most and least expensive roots,
# that way the cost of connecting 'node 1' will by definition not by
# minimum
min_root = {"node": None, "weight": math.inf}
max_root = {"node": None, "weight": -math.inf}
min_root = {"node": None, weight: math.inf}
max_root = {"node": None, weight: -math.inf}
for u, v, d in G.edges(n, data=True):
if d[weight] < min_root["weight"]:
min_root = {"node": v, "weight": d[weight]}
if d[weight] > max_root["weight"]:
max_root = {"node": v, "weight": d[weight]}
if d[weight] < min_root[weight]:
min_root = {"node": v, weight: d[weight]}
if d[weight] > max_root[weight]:
max_root = {"node": v, weight: d[weight]}

min_in_edge = min(G.in_edges(n, data=True), key=lambda x: x[2][weight])
min_root["weight"] = min_root["weight"] + min_in_edge[2][weight]
max_root["weight"] = max_root["weight"] + min_in_edge[2][weight]
min_root[weight] = min_root[weight] + min_in_edge[2][weight]
max_root[weight] = max_root[weight] + min_in_edge[2][weight]

min_arb_weight = math.inf
for arb in nx.ArborescenceIterator(G_1):
arb_weight = arb.size(weight)
if min_arb_weight == math.inf:
min_arb_weight = arb_weight
elif arb_weight > min_arb_weight + max_root["weight"] - min_root["weight"]:
elif arb_weight > min_arb_weight + max_root[weight] - min_root[weight]:
break
# We have to pick the root node of the arborescence for the out
# edge of the first vertex as that is the only node without an
Expand All @@ -477,29 +477,56 @@ def k_pi():
arb.add_edge(n, N, **{weight: G[n][N][weight]})
arb_weight += G[n][N][weight]
break

# We can pick the minimum weight in-edge for the vertex with
# a cycle. If there are multiple edges with the same, minimum
# weight, We need to add all of them.
#
# Delete the edge (N, v) so that we cannot pick it.
edge_data = G[N][n]
G.remove_edge(N, n)
min_weight = min(G.in_edges(n, data=weight), key=lambda x: x[2])[2]
min_edges = [
(u, v, d) for u, v, d in G.in_edges(n, data=weight) if d == min_weight
]
for u, v, d in min_edges:
new_arb = arb.copy()
new_arb.add_edge(u, v, **{weight: d})
new_arb_weight = arb_weight + d
# Check to see the weight of the arborescence, if it is a
# new minimum, clear all of the old potential minimum
# 1-arborescences and add this is the only one. If its
# weight is above the known minimum, do not add it.
if new_arb_weight < minimum_1_arborescence_weight:
minimum_1_arborescences.clear()
minimum_1_arborescence_weight = new_arb_weight
# We have a 1-arborescence, add it to the set
if new_arb_weight == minimum_1_arborescence_weight:
minimum_1_arborescences.add(new_arb)
G.add_edge(N, n, **edge_data)
# We can pick the minimum weight in-edge for the vertex with a
# cycle
min_in_edge_weight = {weight: math.inf}
min_in_edge = None
for u, v, d in G.in_edges(n, data=True):
edge_weight = d[weight]
if u == N:
continue
if edge_weight < min_in_edge_weight[weight]:
min_in_edge_weight[weight] = edge_weight
min_in_edge = (u, v)
arb.add_edge(min_in_edge[0], min_in_edge[1], **d)
arb_weight += min_in_edge_weight[weight]
# Check to see the weight of the arborescence, if it is a new
# minimum, clear all of the old potential minimum
# 1-arborescences and add this is the only one. If its weight is
# above the known minimum, do not add it.
if arb_weight < minimum_1_arborescence_weight:
minimum_1_arborescences.clear()
minimum_1_arborescence_weight = arb_weight
# We have a 1-arborescence, add it to the set
if arb_weight == minimum_1_arborescence_weight:
minimum_1_arborescences.add(arb)
# min_in_edge_weight = {weight: math.inf}
# min_in_edge = None
# for u, v, d in G.in_edges(n, data=True):
# edge_weight = d[weight]
# if u == N:
# continue
# if edge_weight < min_in_edge_weight[weight]:
# min_in_edge_weight[weight] = edge_weight
# min_in_edge = (u, v)
# arb.add_edge(min_in_edge[0], min_in_edge[1], **d)
# arb_weight += min_in_edge_weight[weight]
# # Check to see the weight of the arborescence, if it is a new
# # minimum, clear all of the old potential minimum
# # 1-arborescences and add this is the only one. If its weight is
# # above the known minimum, do not add it.
# if arb_weight < minimum_1_arborescence_weight:
# minimum_1_arborescences.clear()
# minimum_1_arborescence_weight = arb_weight
# # We have a 1-arborescence, add it to the set
# if arb_weight == minimum_1_arborescence_weight:
# minimum_1_arborescences.add(arb)

return minimum_1_arborescences

Expand Down Expand Up @@ -576,7 +603,7 @@ def direction_of_ascent():
minimum_1_arborescences
):
# There is no direction of ascent
return None, min_k_d
return None, minimum_1_arborescences

# 5. GO TO 2

Expand Down Expand Up @@ -648,11 +675,43 @@ def find_epsilon(k, d):
for u, v, d in G.edges(data=True):
d[weight] = original_edge_weights[(u, v)] + pi_dict[u]
dir_ascent, k_d = direction_of_ascent()

# Write the original edge weights back to G
for u, v, d in k_d.edges(data=True):
# k_d is no longer an individual 1-arborescence but rather a set of
# minimal 1-arborescences at the maximum point of the polytope and should
# be reflected as such
k_max = k_d

# Search for a cycle within k_max. If a cycle exists, remove all non cycles
# If no cycle exists, do not change the set
tours = set()
for k in k_max:
if len([n for n in k if k.degree(n) == 2]) == G.order():
# Tour found
tours.add(k)
# Update k_max
k_max = tours if tours != set() else k_max

# Write the original edge weights back to G and every member of k_max at
# the maximum point. Also average the number of times that edge appears in
# the set of minimal 1-arborescences.
x_star = {}
size_k_max = len(k_max)
for u, v, d in G.edges(data=True):
edge_count = 0
d[weight] = original_edge_weights[(u, v)]
return k_d
for k in k_max:
if (u, v) in k.edges():
edge_count += 1
k[u][v][weight] = original_edge_weights[(u, v)]
x_star[(u, v)] = edge_count / size_k_max
# Now symmetrize the edges in x_star and scale them according to (5) in
# reference [1]
z_star = {}
scale_factor = (G.order() - 1) / G.order()
for u, v in x_star.keys():
z_star[(u, v)] = scale_factor * (x_star[(u, v)] + x_star[(v, u)])
del x_star
# Return the optimal weight and the z_star dict
return next(k_max.__iter__()).size(weight), z_star


def held_karp_branch_bound(G, weight="weight"):
Expand Down Expand Up @@ -764,24 +823,24 @@ def k_pi(partition=None):
# plus the difference between the most and least expensive roots,
# that way the cost of connecting 'node 1' will by definition not by
# minimum
min_root = {"node": None, "weight": math.inf}
max_root = {"node": None, "weight": -math.inf}
min_root = {"node": None, weight: math.inf}
max_root = {"node": None, weight: -math.inf}
for u, v, d in G.edges(n, data=True):
if d[weight] < min_root["weight"]:
min_root = {"node": v, "weight": d[weight]}
if d[weight] > max_root["weight"]:
max_root = {"node": v, "weight": d[weight]}
if d[weight] < min_root[weight]:
min_root = {"node": v, weight: d[weight]}
if d[weight] > max_root[weight]:
max_root = {"node": v, weight: d[weight]}

min_in_edge = min(G.in_edges(n, data=True), key=lambda x: x[2][weight])
min_root["weight"] = min_root["weight"] + min_in_edge[2][weight]
max_root["weight"] = max_root["weight"] + min_in_edge[2][weight]
min_root[weight] = min_root[weight] + min_in_edge[2][weight]
max_root[weight] = max_root[weight] + min_in_edge[2][weight]

min_arb_weight = math.inf
for arb in nx.ArborescenceIterator(G_1, init_partition=partition):
arb_weight = arb.size(weight)
if min_arb_weight == math.inf:
min_arb_weight = arb_weight
elif arb_weight > min_arb_weight + max_root["weight"] - min_root["weight"]:
elif arb_weight > min_arb_weight + max_root[weight] - min_root[weight]:
break
# We have to pick the root node of the arborescence for the out
# edge of the first vertex as that is the only node without an
Expand Down

0 comments on commit 37d6219

Please sign in to comment.