Source code for dwave_networkx.drawing.chimera_layout

# Copyright 2018 D-Wave Systems Inc.
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.
#
# ================================================================================================
"""
Tools to visualize Chimera lattices and weighted graph problems on them.
"""

from __future__ import division

import networkx as nx
from networkx import draw

from dwave_networkx import _PY2
from dwave_networkx.drawing.qubit_layout import draw_qubit_graph, draw_embedding, draw_yield
from dwave_networkx.generators.chimera import chimera_graph, find_chimera_indices, chimera_coordinates

# compatibility for python 2/3
if _PY2:
    range = xrange

    def itervalues(d): return d.itervalues()

    def iteritems(d): return d.iteritems()
else:
    def itervalues(d): return d.values()

    def iteritems(d): return d.items()

__all__ = ['chimera_layout', 'draw_chimera', 'draw_chimera_embedding', 'draw_chimera_yield']


[docs]def chimera_layout(G, scale=1., center=None, dim=2): """Positions the nodes of graph G in a Chimera cross topology. NumPy (http://scipy.org) is required for this function. Parameters ---------- G : NetworkX graph Should be a Chimera graph or a subgraph of a Chimera graph. If every node in G has a `chimera_index` attribute, those are used to place the nodes. Otherwise makes a best-effort attempt to find positions. scale : float (default 1.) Scale factor. When scale = 1, all positions fit within [0, 1] on the x-axis and [-1, 0] on the y-axis. center : None or array (default None) Coordinates of the top left corner. dim : int (default 2) Number of dimensions. When dim > 2, all extra dimensions are set to 0. Returns ------- pos : dict A dictionary of positions keyed by node. Examples -------- >>> G = dnx.chimera_graph(1) >>> pos = dnx.chimera_layout(G) """ if not isinstance(G, nx.Graph): empty_graph = nx.Graph() empty_graph.add_edges_from(G) G = empty_graph # now we get chimera coordinates for the translation # first, check if we made it if G.graph.get("family") == "chimera": m = G.graph['rows'] n = G.graph['columns'] t = G.graph['tile'] # get a node placement function xy_coords = chimera_node_placer_2d(m, n, t, scale, center, dim) if G.graph.get('labels') == 'coordinate': pos = {v: xy_coords(*v) for v in G.nodes()} elif G.graph.get('data'): pos = {v: xy_coords(*dat['chimera_index']) for v, dat in G.nodes(data=True)} else: coord = chimera_coordinates(m, n, t) pos = {v: xy_coords(*coord.linear_to_chimera(v)) for v in G.nodes()} else: # best case scenario, each node in G has a chimera_index attribute. Otherwise # we will try to determine it using the find_chimera_indices function. if all('chimera_index' in dat for __, dat in G.nodes(data=True)): chimera_indices = {v: dat['chimera_index'] for v, dat in G.nodes(data=True)} else: chimera_indices = find_chimera_indices(G) # we could read these off of the name attribute for G, but we would want the values in # the nodes to override the name in case of conflict. m = max(idx[0] for idx in itervalues(chimera_indices)) + 1 n = max(idx[1] for idx in itervalues(chimera_indices)) + 1 t = max(idx[3] for idx in itervalues(chimera_indices)) + 1 xy_coords = chimera_node_placer_2d(m, n, t, scale, center, dim) # compute our coordinates pos = {v: xy_coords(i, j, u, k) for v, (i, j, u, k) in iteritems(chimera_indices)} return pos
def chimera_node_placer_2d(m, n, t, scale=1., center=None, dim=2): """Generates a function that converts Chimera indices to x, y coordinates for a plot. Parameters ---------- m : int Number of rows in the Chimera lattice. n : int Number of columns in the Chimera lattice. t : int Size of the shore within each Chimera tile. scale : float (default 1.) Scale factor. When scale = 1, all positions fit within [0, 1] on the x-axis and [-1, 0] on the y-axis. center : None or array (default None) Coordinates of the top left corner. dim : int (default 2) Number of dimensions. When dim > 2, all extra dimensions are set to 0. Returns ------- xy_coords : function A function that maps a Chimera index (i, j, u, k) in an (m, n, t) Chimera lattice to x,y coordinates such as used by a plot. """ import numpy as np tile_center = t // 2 tile_length = t + 3 # 1 for middle of cross, 2 for spacing between tiles # want the enter plot to fill in [0, 1] when scale=1 scale /= max(m, n) * tile_length - 3 grid_offsets = {} if center is None: center = np.zeros(dim) else: center = np.asarray(center) paddims = dim - 2 if paddims < 0: raise ValueError("layout must have at least two dimensions") if len(center) != dim: raise ValueError("length of center coordinates must match dimension of layout") def _xy_coords(i, j, u, k): # row, col, shore, shore index # first get the coordinatiates within the tile if k < tile_center: p = k else: p = k + 1 if u: xy = np.array([tile_center, -1 * p]) else: xy = np.array([p, -1 * tile_center]) # next offset the corrdinates based on the which tile if i > 0 or j > 0: if (i, j) in grid_offsets: xy += grid_offsets[(i, j)] else: off = np.array([j * tile_length, -1 * i * tile_length]) xy += off grid_offsets[(i, j)] = off # convention for Chimera-lattice pictures is to invert the y-axis return np.hstack((xy * scale, np.zeros(paddims))) + center return _xy_coords
[docs]def draw_chimera(G, **kwargs): """Draws graph G in a Chimera cross topology. If `linear_biases` and/or `quadratic_biases` are provided, these are visualized on the plot. Parameters ---------- G : NetworkX graph Should be a Chimera graph or a subgraph of a Chimera graph. linear_biases : dict (optional, default {}) A dict of biases associated with each node in G. Should be of form {node: bias, ...}. Each bias should be numeric. quadratic_biases : dict (optional, default {}) A dict of biases associated with each edge in G. Should be of form {edge: bias, ...}. Each bias should be numeric. Self-loop edges (i.e., :math:`i=j`) are treated as linear biases. kwargs : optional keywords See networkx.draw_networkx() for a description of optional keywords, with the exception of the `pos` parameter which is not used by this function. If `linear_biases` or `quadratic_biases` are provided, any provided `node_color` or `edge_color` arguments are ignored. Examples -------- >>> # Plot 2x2 Chimera unit cells >>> import networkx as nx >>> import dwave_networkx as dnx >>> import matplotlib.pyplot as plt # doctest: +SKIP >>> G = dnx.chimera_graph(2, 2, 4) >>> dnx.draw_chimera(G) # doctest: +SKIP >>> plt.show() # doctest: +SKIP """ draw_qubit_graph(G, chimera_layout(G), **kwargs)
def draw_chimera_embedding(G, *args, **kwargs): """Draws an embedding onto the chimera graph G, according to layout. If interaction_edges is not None, then only display the couplers in that list. If embedded_graph is not None, the only display the couplers between chains with intended couplings according to embedded_graph. Parameters ---------- G : NetworkX graph Should be a Chimera graph or a subgraph of a Chimera graph. emb : dict A dict of chains associated with each node in G. Should be of the form {node: chain, ...}. Chains should be iterables of qubit labels (qubits are nodes in G). embedded_graph : NetworkX graph (optional, default None) A graph which contains all keys of emb as nodes. If specified, edges of G will be considered interactions if and only if they exist between two chains of emb if their keys are connected by an edge in embedded_graph interaction_edges : list (optional, default None) A list of edges which will be used as interactions. show_labels: boolean (optional, default False) If show_labels is True, then each chain in emb is labelled with its key. chain_color : dict (optional, default None) A dict of colors associated with each key in emb. Should be of the form {node: rgba_color, ...}. Colors should be length-4 tuples of floats between 0 and 1 inclusive. If chain_color is None, each chain will be assigned a different color. unused_color : tuple (optional, default (0.9,0.9,0.9,1.0)) The color to use for nodes and edges of G which are not involved in chains, and edges which are neither chain edges nor interactions. If unused_color is None, these nodes and edges will not be shown at all. overlapped_embedding: boolean (optional, default False) If overlapped_embedding is True, then chains in emb may overlap (contain the same vertices in G), and the drawing will display these overlaps as concentric circles. kwargs : optional keywords See networkx.draw_networkx() for a description of optional keywords, with the exception of the `pos` parameter which is not used by this function. If `linear_biases` or `quadratic_biases` are provided, any provided `node_color` or `edge_color` arguments are ignored. """ draw_embedding(G, chimera_layout(G), *args, **kwargs) def draw_chimera_yield(G, **kwargs): """Draws the given graph G with highlighted faults, according to layout. Parameters ---------- G : NetworkX graph The graph to be parsed for faults unused_color : tuple or color string (optional, default (0.9,0.9,0.9,1.0)) The color to use for nodes and edges of G which are not faults. If unused_color is None, these nodes and edges will not be shown at all. fault_color : tuple or color string (optional, default (1.0,0.0,0.0,1.0)) A color to represent nodes absent from the graph G. Colors should be length-4 tuples of floats between 0 and 1 inclusive. fault_shape : string, optional (default='x') The shape of the fault nodes. Specification is as matplotlib.scatter marker, one of 'so^>v<dph8'. fault_style : string, optional (default='dashed') Edge fault line style (solid|dashed|dotted,dashdot) kwargs : optional keywords See networkx.draw_networkx() for a description of optional keywords, with the exception of the `pos` parameter which is not used by this function. If `linear_biases` or `quadratic_biases` are provided, any provided `node_color` or `edge_color` arguments are ignored. """ try: assert(G.graph["family"] == "chimera") m = G.graph["rows"] n = G.graph["columns"] t = G.graph["tile"] coordinates = G.graph["labels"] == "coordinate" except: raise ValueError("Target chimera graph needs to have columns, rows, \ tile, and label attributes to be able to identify faulty qubits.") perfect_graph = chimera_graph(m,n,t, coordinates=coordinates) draw_yield(G, chimera_layout(perfect_graph), perfect_graph, **kwargs)