| 5514 | def distance_graph(self, dist): |
| 5515 | r""" |
| 5516 | Returns the graph on the same vertex set as |
| 5517 | the original graph but vertices are adjacent |
| 5518 | in the returned graph if and only if they are |
| 5519 | at specified distances in the original graph. |
| 5520 | |
| 5521 | INPUT: |
| 5522 | |
| 5523 | - ``dist`` is a nonnegative integer or |
| 5524 | a list of nonnegative integers. |
| 5525 | ``Infinity`` may be used here to describe |
| 5526 | vertex pairs in separate components. |
| 5527 | |
| 5528 | OUTPUT: |
| 5529 | |
| 5530 | The returned value is an undirected graph. The |
| 5531 | vertex set is identical to the calling graph, but edges |
| 5532 | of the returned graph join vertices whose distance in |
| 5533 | the calling graph are present in the input ``dist``. |
| 5534 | Loops will only be present if distance 0 is included. If |
| 5535 | the original graph has a position dictionary specifying |
| 5536 | locations of vertices for plotting, then this information |
| 5537 | is copied over to the distance graph. In some instances |
| 5538 | this layout may not be the best, and might even be confusing |
| 5539 | when edges run on top of each other due to symmetries |
| 5540 | chosen for the layout. |
| 5541 | |
| 5542 | EXAMPLES:: |
| 5543 | |
| 5544 | sage: G = graphs.CompleteGraph(3) |
| 5545 | sage: H = G.cartesian_product(graphs.CompleteGraph(2)) |
| 5546 | sage: K = H.distance_graph(2) |
| 5547 | sage: K.am() |
| 5548 | [0 0 0 1 0 1] |
| 5549 | [0 0 1 0 1 0] |
| 5550 | [0 1 0 0 0 1] |
| 5551 | [1 0 0 0 1 0] |
| 5552 | [0 1 0 1 0 0] |
| 5553 | [1 0 1 0 0 0] |
| 5554 | |
| 5555 | To obtain the graph where vertices are adjacent if their |
| 5556 | distance apart is ``d`` or less use a ``range()`` command |
| 5557 | to create the input, using ``d+1`` as the input to ``range``. |
| 5558 | Notice that this will include distance 0 and hence place a loop |
| 5559 | at each vertex. To avoid this, use ``range(1,d+1)``. :: |
| 5560 | |
| 5561 | sage: G = graphs.OddGraph(4) |
| 5562 | sage: d = G.diameter() |
| 5563 | sage: n = G.num_verts() |
| 5564 | sage: H = G.distance_graph(range(d+1)) |
| 5565 | sage: H.is_isomorphic(graphs.CompleteGraph(n)) |
| 5566 | False |
| 5567 | sage: H = G.distance_graph(range(1,d+1)) |
| 5568 | sage: H.is_isomorphic(graphs.CompleteGraph(n)) |
| 5569 | True |
| 5570 | |
| 5571 | A complete collection of distance graphs will have |
| 5572 | adjacency matrices that sum to the matrix of all ones. :: |
| 5573 | |
| 5574 | sage: P = graphs.PathGraph(20) |
| 5575 | sage: all_ones = sum([P.distance_graph(i).am() for i in range(20)]) |
| 5576 | sage: all_ones == matrix(ZZ, 20, 20, [1]*400) |
| 5577 | True |
| 5578 | |
| 5579 | Four-bit strings differing in one bit is the same as |
| 5580 | four-bit strings differing in three bits. :: |
| 5581 | |
| 5582 | sage: G = graphs.CubeGraph(4) |
| 5583 | sage: H = G.distance_graph(3) |
| 5584 | sage: G.is_isomorphic(H) |
| 5585 | True |
| 5586 | |
| 5587 | The graph of eight-bit strings, adjacent if different |
| 5588 | in an odd number of bits. :: |
| 5589 | |
| 5590 | sage: G = graphs.CubeGraph(8) |
| 5591 | sage: H = G.distance_graph([1,3,5,7]) |
| 5592 | sage: degrees = [0]*sum([binomial(8,j) for j in [1,3,5,7]]) |
| 5593 | sage: degrees.append(2^8) |
| 5594 | sage: degrees == H.degree_histogram() |
| 5595 | True |
| 5596 | |
| 5597 | An example of using ``Infinity`` as the distance in |
| 5598 | a graph that is not connected. :: |
| 5599 | |
| 5600 | sage: G = graphs.CompleteGraph(3) |
| 5601 | sage: H = G.disjoint_union(graphs.CompleteGraph(2)) |
| 5602 | sage: L = H.distance_graph(Infinity) |
| 5603 | sage: L.am() |
| 5604 | [0 0 0 1 1] |
| 5605 | [0 0 0 1 1] |
| 5606 | [0 0 0 1 1] |
| 5607 | [1 1 1 0 0] |
| 5608 | [1 1 1 0 0] |
| 5609 | |
| 5610 | TESTS: |
| 5611 | |
| 5612 | Empty input, or unachievable distances silently yield empty graphs. :: |
| 5613 | |
| 5614 | sage: G = graphs.CompleteGraph(5) |
| 5615 | sage: G.distance_graph([]).num_edges() |
| 5616 | 0 |
| 5617 | sage: G = graphs.CompleteGraph(5) |
| 5618 | sage: G.distance_graph(23).num_edges() |
| 5619 | 0 |
| 5620 | |
| 5621 | It is an error to provide a distance that is not an integer type. :: |
| 5622 | |
| 5623 | sage: G = graphs.CompleteGraph(5) |
| 5624 | sage: G.distance_graph('junk') |
| 5625 | Traceback (most recent call last): |
| 5626 | ... |
| 5627 | TypeError: unable to convert x (=junk) to an integer |
| 5628 | |
| 5629 | It is an error to provide a negative distance. :: |
| 5630 | |
| 5631 | sage: G = graphs.CompleteGraph(5) |
| 5632 | sage: G.distance_graph(-3) |
| 5633 | Traceback (most recent call last): |
| 5634 | ... |
| 5635 | ValueError: Distance graph for a negative distance (d=-3) is not defined |
| 5636 | |
| 5637 | AUTHOR: |
| 5638 | |
| 5639 | Rob Beezer, 2009-11-25 |
| 5640 | """ |
| 5641 | from sage.rings.infinity import Infinity |
| 5642 | from copy import copy |
| 5643 | # If input is not a list, make a list with this single object |
| 5644 | if not isinstance(dist, list): |
| 5645 | dist = [dist] |
| 5646 | # Create a list of positive integer (or infinite) distances |
| 5647 | distances = [] |
| 5648 | for d in dist: |
| 5649 | if d == Infinity: |
| 5650 | distances.append(d) |
| 5651 | else: |
| 5652 | dint = Integer(d) |
| 5653 | if dint < 0: |
| 5654 | raise ValueError('Distance graph for a negative distance (d=%d) is not defined' % dint) |
| 5655 | distances.append(dint) |
| 5656 | # Build a graph on the same vertex set, with loops for distance 0 |
| 5657 | vertices = {} |
| 5658 | for v in self.vertex_iterator(): |
| 5659 | vertices[v] = {} |
| 5660 | positions = copy(self.get_pos()) |
| 5661 | if Integer(0) in distances: |
| 5662 | looped = True |
| 5663 | else: |
| 5664 | looped = False |
| 5665 | D = Graph(vertices, pos=positions, multiedges=False, loops=looped) |
| 5666 | if len(distances) == 1: |
| 5667 | dstring = "distance " + str(distances[0]) |
| 5668 | else: |
| 5669 | dstring = "distances " + str(sorted(distances)) |
| 5670 | D.name("Distance graph for %s in " % dstring + self.name()) |
| 5671 | # Create the appropriate edges |
| 5672 | # Using shortest_path_all_pairs() here is much slower, see Trac 7533 |
| 5673 | for u in self.vertex_iterator(): |
| 5674 | for v in self.vertex_iterator(): |
| 5675 | if self.distance(u,v) in distances: |
| 5676 | D.add_edge(u,v) |
| 5677 | return D |
| 5678 | |