def test_gnp_augmentation(): rng = random.Random(0) G = nx.gnp_random_graph(30, 0.005, seed=0) # Randomly make edges available avail = {(u, v): 1 + rng.random() for u, v in complement_edges(G) if rng.random() < .25} _check_augmentations(G, avail)
def test_gnp_augmentation(): rng = random.Random(0) G = nx.gnp_random_graph(30, 0.005, seed=0) # Randomly make edges available avail = {(u, v): 1 + rng.random() for u, v in complement_edges(G) if rng.random() < .25} _check_augmentations(G, avail)
def test_weight_key(): G = nx.Graph() G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8, 9]) G.add_edges_from([(3, 8), (1, 2), (2, 3)]) impossible = {(3, 6), (3, 9)} rng = random.Random(0) avail_uv = list(set(complement_edges(G)) - impossible) avail = [(u, v, {'cost': rng.random()}) for u, v in avail_uv] _augment_and_check(G, k=1) _augment_and_check(G, k=1, avail=avail_uv) _augment_and_check(G, k=1, avail=avail, weight='cost') _check_augmentations(G, avail, weight='cost')
def test_weight_key(): G = nx.Graph() G.add_nodes_from([ 1, 2, 3, 4, 5, 6, 7, 8, 9]) G.add_edges_from([(3, 8), (1, 2), (2, 3)]) impossible = {(3, 6), (3, 9)} rng = random.Random(0) avail_uv = list(set(complement_edges(G)) - impossible) avail = [(u, v, {'cost': rng.random()}) for u, v in avail_uv] _augment_and_check(G, k=1) _augment_and_check(G, k=1, avail=avail_uv) _augment_and_check(G, k=1, avail=avail, weight='cost') _check_augmentations(G, avail, weight='cost')
def _check_augmentations(G, avail=None, max_k=None, weight=None, verbose=False): """ Helper to check weighted/unweighted cases with multiple values of k """ # Using all available edges, find the maximum edge-connectivity try: orig_k = nx.edge_connectivity(G) except nx.NetworkXPointlessConcept: orig_k = 0 if avail is not None: all_aug_edges = _unpack_available_edges(avail, weight=weight)[0] G_aug_all = G.copy() G_aug_all.add_edges_from(all_aug_edges) try: max_aug_k = nx.edge_connectivity(G_aug_all) except nx.NetworkXPointlessConcept: max_aug_k = 0 else: max_aug_k = G.number_of_nodes() - 1 if max_k is None: max_k = min(4, max_aug_k) avail_uniform = {e: 1 for e in complement_edges(G)} if verbose: print('\n=== CHECK_AUGMENTATION ===') print('G.number_of_nodes = {!r}'.format(G.number_of_nodes())) print('G.number_of_edges = {!r}'.format(G.number_of_edges())) print('max_k = {!r}'.format(max_k)) print('max_aug_k = {!r}'.format(max_aug_k)) print('orig_k = {!r}'.format(orig_k)) # check augmentation for multiple values of k for k in range(1, max_k + 1): if verbose: print('---------------') print('Checking k = {}'.format(k)) # Check the unweighted version if verbose: print('unweighted case') aug_edges1, info1 = _augment_and_check(G, k=k, verbose=verbose, orig_k=orig_k) # Check that the weighted version with all available edges and uniform # weights gives a similar solution to the unweighted case. if verbose: print('weighted uniform case') aug_edges2, info2 = _augment_and_check(G, k=k, avail=avail_uniform, verbose=verbose, orig_k=orig_k, max_aug_k=G.number_of_nodes() - 1) # Check the weighted version if avail is not None: if verbose: print('weighted case') aug_edges3, info3 = _augment_and_check(G, k=k, avail=avail, weight=weight, verbose=verbose, max_aug_k=max_aug_k, orig_k=orig_k) if aug_edges1 is not None: # Check approximation ratios if k == 1: # when k=1, both solutions should be optimal assert_equal(info2['total_weight'], info1['total_weight']) if k == 2: # when k=2, the weighted version is an approximation if orig_k == 0: # the approximation ratio is 3 if G is not connected assert_less_equal(info2['total_weight'], info1['total_weight'] * 3) else: # the approximation ratio is 2 if G is was connected assert_less_equal(info2['total_weight'], info1['total_weight'] * 2) _check_unconstrained_bridge_property(G, info1)
def _augment_and_check(G, k, avail=None, weight=None, verbose=False, orig_k=None, max_aug_k=None): """ Does one specific augmentation and checks for properties of the result """ if orig_k is None: try: orig_k = nx.edge_connectivity(G) except nx.NetworkXPointlessConcept: orig_k = 0 info = {} try: if avail is not None: # ensure avail is in dict form avail_dict = dict( zip(*_unpack_available_edges(avail, weight=weight))) else: avail_dict = None try: # Find the augmentation if possible generator = nx.k_edge_augmentation(G, k=k, weight=weight, avail=avail) assert_false(isinstance(generator, list), 'should always return an iter') aug_edges = [] for edge in generator: aug_edges.append(edge) except nx.NetworkXUnfeasible: infeasible = True info['infeasible'] = True assert_equal(len(aug_edges), 0, 'should not generate anything if unfeasible') if avail is None: n_nodes = G.number_of_nodes() assert_less_equal( n_nodes, k, ('unconstrained cases are only unfeasible if |V| <= k. ' 'Got |V|={} and k={}'.format(n_nodes, k))) else: if max_aug_k is None: G_aug_all = G.copy() G_aug_all.add_edges_from(avail_dict.keys()) try: max_aug_k = nx.edge_connectivity(G_aug_all) except nx.NetworkXPointlessConcept: max_aug_k = 0 assert_less( max_aug_k, k, ('avail should only be unfeasible if using all edges ' 'does not achieve k-edge-connectivity')) # Test for a partial solution partial_edges = list( nx.k_edge_augmentation(G, k=k, weight=weight, partial=True, avail=avail)) info['n_partial_edges'] = len(partial_edges) if avail_dict is None: assert_equal( set(partial_edges), set(complement_edges(G)), ('unweighted partial solutions should be the complement')) elif len(avail_dict) > 0: H = G.copy() # Find the partial / full augmented connectivity H.add_edges_from(partial_edges) partial_conn = nx.edge_connectivity(H) H.add_edges_from(set(avail_dict.keys())) full_conn = nx.edge_connectivity(H) # Full connectivity should be no better than our partial # solution. assert_equal(partial_conn, full_conn, 'adding more edges should not increase k-conn') # Find the new edge-connectivity after adding the augmenting edges aug_edges = partial_edges else: infeasible = False # Find the weight of the augmentation num_edges = len(aug_edges) if avail is not None: total_weight = sum([avail_dict[e] for e in aug_edges]) else: total_weight = num_edges info['total_weight'] = total_weight info['num_edges'] = num_edges # Find the new edge-connectivity after adding the augmenting edges G_aug = G.copy() G_aug.add_edges_from(aug_edges) try: aug_k = nx.edge_connectivity(G_aug) except nx.NetworkXPointlessConcept: aug_k = 0 info['aug_k'] = aug_k # Do checks if not infeasible and orig_k < k: assert_greater_equal( info['aug_k'], k, ('connectivity should increase to k={} or more'.format(k))) assert_greater_equal(info['aug_k'], orig_k, ('augmenting should never reduce connectivity')) _assert_solution_properties(G, aug_edges, avail_dict) except Exception: info['failed'] = True print('edges = {}'.format(list(G.edges()))) print('nodes = {}'.format(list(G.nodes()))) print('aug_edges = {}'.format(list(aug_edges))) print('info = {}'.format(info)) raise else: if verbose: print('info = {}'.format(info)) if infeasible: aug_edges = None return aug_edges, info
def complement_edges(G): from networkx.algorithms.connectivity.edge_augmentation import complement_edges return it.starmap(e_, complement_edges(G))
def _augment_and_check(G, k, avail=None, weight=None, verbose=False, orig_k=None, max_aug_k=None): """ Does one specific augmentation and checks for properties of the result """ if orig_k is None: try: orig_k = nx.edge_connectivity(G) except nx.NetworkXPointlessConcept: orig_k = 0 info = {} try: if avail is not None: # ensure avail is in dict form avail_dict = dict( zip(*_unpack_available_edges(avail, weight=weight))) else: avail_dict = None try: # Find the augmentation if possible generator = nx.k_edge_augmentation(G, k=k, weight=weight, avail=avail) assert not isinstance(generator, list), "should always return an iter" aug_edges = [] for edge in generator: aug_edges.append(edge) except nx.NetworkXUnfeasible: infeasible = True info["infeasible"] = True assert len( aug_edges) == 0, "should not generate anything if unfeasible" if avail is None: n_nodes = G.number_of_nodes() assert n_nodes <= k, ( "unconstrained cases are only unfeasible if |V| <= k. " f"Got |V|={n_nodes} and k={k}") else: if max_aug_k is None: G_aug_all = G.copy() G_aug_all.add_edges_from(avail_dict.keys()) try: max_aug_k = nx.edge_connectivity(G_aug_all) except nx.NetworkXPointlessConcept: max_aug_k = 0 assert max_aug_k < k, ( "avail should only be unfeasible if using all edges " "does not achieve k-edge-connectivity") # Test for a partial solution partial_edges = list( nx.k_edge_augmentation(G, k=k, weight=weight, partial=True, avail=avail)) info["n_partial_edges"] = len(partial_edges) if avail_dict is None: assert set(partial_edges) == set( complement_edges(G) ), "unweighted partial solutions should be the complement" elif len(avail_dict) > 0: H = G.copy() # Find the partial / full augmented connectivity H.add_edges_from(partial_edges) partial_conn = nx.edge_connectivity(H) H.add_edges_from(set(avail_dict.keys())) full_conn = nx.edge_connectivity(H) # Full connectivity should be no better than our partial # solution. assert (partial_conn == full_conn ), "adding more edges should not increase k-conn" # Find the new edge-connectivity after adding the augmenting edges aug_edges = partial_edges else: infeasible = False # Find the weight of the augmentation num_edges = len(aug_edges) if avail is not None: total_weight = sum([avail_dict[e] for e in aug_edges]) else: total_weight = num_edges info["total_weight"] = total_weight info["num_edges"] = num_edges # Find the new edge-connectivity after adding the augmenting edges G_aug = G.copy() G_aug.add_edges_from(aug_edges) try: aug_k = nx.edge_connectivity(G_aug) except nx.NetworkXPointlessConcept: aug_k = 0 info["aug_k"] = aug_k # Do checks if not infeasible and orig_k < k: assert info[ "aug_k"] >= k, f"connectivity should increase to k={k} or more" assert info[ "aug_k"] >= orig_k, "augmenting should never reduce connectivity" _assert_solution_properties(G, aug_edges, avail_dict) except Exception: info["failed"] = True print(f"edges = {list(G.edges())}") print(f"nodes = {list(G.nodes())}") print(f"aug_edges = {list(aug_edges)}") print(f"info = {info}") raise else: if verbose: print(f"info = {info}") if infeasible: aug_edges = None return aug_edges, info
def _check_augmentations(G, avail=None, max_k=None, weight=None, verbose=False): """ Helper to check weighted/unweighted cases with multiple values of k """ # Using all available edges, find the maximum edge-connectivity try: orig_k = nx.edge_connectivity(G) except nx.NetworkXPointlessConcept: orig_k = 0 if avail is not None: all_aug_edges = _unpack_available_edges(avail, weight=weight)[0] G_aug_all = G.copy() G_aug_all.add_edges_from(all_aug_edges) try: max_aug_k = nx.edge_connectivity(G_aug_all) except nx.NetworkXPointlessConcept: max_aug_k = 0 else: max_aug_k = G.number_of_nodes() - 1 if max_k is None: max_k = min(4, max_aug_k) avail_uniform = {e: 1 for e in complement_edges(G)} if verbose: print('\n=== CHECK_AUGMENTATION ===') print('G.number_of_nodes = {!r}'.format(G.number_of_nodes())) print('G.number_of_edges = {!r}'.format(G.number_of_edges())) print('max_k = {!r}'.format(max_k)) print('max_aug_k = {!r}'.format(max_aug_k)) print('orig_k = {!r}'.format(orig_k)) # check augmentation for multiple values of k for k in range(1, max_k + 1): if verbose: print('---------------') print('Checking k = {}'.format(k)) # Check the unweighted version if verbose: print('unweighted case') aug_edges1, info1 = _augment_and_check( G, k=k, verbose=verbose, orig_k=orig_k) # Check that the weighted version with all available edges and uniform # weights gives a similar solution to the unweighted case. if verbose: print('weighted uniform case') aug_edges2, info2 = _augment_and_check( G, k=k, avail=avail_uniform, verbose=verbose, orig_k=orig_k, max_aug_k=G.number_of_nodes() - 1) # Check the weighted version if avail is not None: if verbose: print('weighted case') aug_edges3, info3 = _augment_and_check( G, k=k, avail=avail, weight=weight, verbose=verbose, max_aug_k=max_aug_k, orig_k=orig_k) if aug_edges1 is not None: # Check approximation ratios if k == 1: # when k=1, both solutions should be optimal assert_equal(info2['total_weight'], info1['total_weight']) if k == 2: # when k=2, the weighted version is an approximation if orig_k == 0: # the approximation ratio is 3 if G is not connected assert_less_equal(info2['total_weight'], info1['total_weight'] * 3) else: # the approximation ratio is 2 if G is was connected assert_less_equal(info2['total_weight'], info1['total_weight'] * 2) _check_unconstrained_bridge_property(G, info1)
def _augment_and_check(G, k, avail=None, weight=None, verbose=False, orig_k=None, max_aug_k=None): """ Does one specific augmentation and checks for properties of the result """ if orig_k is None: try: orig_k = nx.edge_connectivity(G) except nx.NetworkXPointlessConcept: orig_k = 0 info = {} try: if avail is not None: # ensure avail is in dict form avail_dict = dict(zip(*_unpack_available_edges(avail, weight=weight))) else: avail_dict = None try: # Find the augmentation if possible generator = nx.k_edge_augmentation(G, k=k, weight=weight, avail=avail) assert_false(isinstance(generator, list), 'should always return an iter') aug_edges = [] for edge in generator: aug_edges.append(edge) except nx.NetworkXUnfeasible: infeasible = True info['infeasible'] = True assert_equal(len(aug_edges), 0, 'should not generate anything if unfeasible') if avail is None: n_nodes = G.number_of_nodes() assert_less_equal(n_nodes, k, ( 'unconstrained cases are only unfeasible if |V| <= k. ' 'Got |V|={} and k={}'.format(n_nodes, k) )) else: if max_aug_k is None: G_aug_all = G.copy() G_aug_all.add_edges_from(avail_dict.keys()) try: max_aug_k = nx.edge_connectivity(G_aug_all) except nx.NetworkXPointlessConcept: max_aug_k = 0 assert_less(max_aug_k, k, ( 'avail should only be unfeasible if using all edges ' 'doesnt acheive k-edge-connectivity')) # Test for a partial solution partial_edges = list(nx.k_edge_augmentation( G, k=k, weight=weight, partial=True, avail=avail)) info['n_partial_edges'] = len(partial_edges) if avail_dict is None: assert_equal(set(partial_edges), set(complement_edges(G)), ( 'unweighted partial solutions should be the complement')) elif len(avail_dict) > 0: H = G.copy() # Find the partial / full augmented connectivity H.add_edges_from(partial_edges) partial_conn = nx.edge_connectivity(H) H.add_edges_from(set(avail_dict.keys())) full_conn = nx.edge_connectivity(H) # Full connectivity should be no better than our partial # solution. assert_equal(partial_conn, full_conn, 'adding more edges should not increase k-conn') # Find the new edge-connectivity after adding the augmenting edges aug_edges = partial_edges else: infeasible = False # Find the weight of the augmentation num_edges = len(aug_edges) if avail is not None: total_weight = sum([avail_dict[e] for e in aug_edges]) else: total_weight = num_edges info['total_weight'] = total_weight info['num_edges'] = num_edges # Find the new edge-connectivity after adding the augmenting edges G_aug = G.copy() G_aug.add_edges_from(aug_edges) try: aug_k = nx.edge_connectivity(G_aug) except nx.NetworkXPointlessConcept: aug_k = 0 info['aug_k'] = aug_k # Do checks if not infeasible and orig_k < k: assert_greater_equal(info['aug_k'], k, ( 'connectivity should increase to k={} or more'.format(k))) assert_greater_equal(info['aug_k'], orig_k, ( 'augmenting should never reduce connectivity')) _assert_solution_properties(G, aug_edges, avail_dict) except Exception: info['failed'] = True print('edges = {}'.format(list(G.edges()))) print('nodes = {}'.format(list(G.nodes()))) print('aug_edges = {}'.format(list(aug_edges))) print('info = {}'.format(info)) raise else: if verbose: print('info = {}'.format(info)) if infeasible: aug_edges = None return aug_edges, info