Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 070454a

Browse files
committedDec 8, 2021
added line_plot option
1 parent a230ce8 commit 070454a

File tree

4 files changed

+391
-45
lines changed

4 files changed

+391
-45
lines changed
 

‎dwave_networkx/drawing/chimera_layout.py

+41-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import networkx as nx
2121
from networkx import draw
2222

23-
from dwave_networkx.drawing.qubit_layout import draw_qubit_graph, draw_embedding, draw_yield, normalize_size_and_aspect
23+
from dwave_networkx.drawing.qubit_layout import draw_qubit_graph, draw_embedding, draw_yield, normalize_size_and_aspect, draw_lineplot
2424
from dwave_networkx.generators.chimera import chimera_graph, find_chimera_indices, chimera_coordinates
2525

2626

@@ -151,7 +151,10 @@ def chimera_node_placer_2d(m, n, t, scale=1., center=None, dim=2, normalize_kwar
151151
"""
152152
import numpy as np
153153

154-
center_pad = 1
154+
line_plot = False if normalize_kwargs is None else normalize_kwargs.get('line_plot')
155+
156+
center_pad = 0 if line_plot else 1
157+
155158
tile_center = t // 2
156159
tile_length = t + 2 + center_pad # 2 for spacing between tiles
157160

@@ -203,7 +206,18 @@ def _xy_coords(i, j, u, k):
203206
# convention for Chimera-lattice pictures is to invert the y-axis
204207
return np.hstack((xy * scale, paddims)) + center
205208

206-
return _xy_coords
209+
if line_plot:
210+
qubit_dx = np.hstack(([(t + 1)/2, 0], paddims)) * scale
211+
qubit_dy = np.hstack(([0, (t + 1)/2], paddims)) * scale
212+
def _line_coords(i, j, u, k):
213+
xy = _xy_coords(i, j, u, k)
214+
if u:
215+
return np.vstack((xy - qubit_dx, xy + qubit_dx))
216+
else:
217+
return np.vstack((xy - qubit_dy, xy + qubit_dy))
218+
return _line_coords
219+
else:
220+
return _xy_coords
207221

208222

209223
def draw_chimera(G, **kwargs):
@@ -226,6 +240,14 @@ def draw_chimera(G, **kwargs):
226240
form {edge: bias, ...}. Each bias should be numeric. Self-loop
227241
edges (i.e., :math:`i=j`) are treated as linear biases.
228242
243+
line_plot : boolean (optional, default False)
244+
If line_plot is True, then qubits are drawn as line segments, and edges
245+
are drawn either as line segments between qubits, or as circles where
246+
two qubits overlap. In this drawing style, the interpretation the width
247+
and node_size parameters (provided in kwargs) determines the area of the
248+
circles, and line widths, respectively. For more information, see
249+
:func:`dwave_networkx.qubit_layout.draw_lineplot`.
250+
229251
kwargs : optional keywords
230252
See networkx.draw_networkx() for a description of optional keywords,
231253
with the exception of the `pos` parameter which is not used by this
@@ -291,6 +313,14 @@ def draw_chimera_embedding(G, *args, **kwargs):
291313
the same vertices in G), and the drawing will display these overlaps as
292314
concentric circles.
293315
316+
line_plot : boolean (optional, default False)
317+
If line_plot is True, then qubits are drawn as line segments, and edges
318+
are drawn either as line segments between qubits, or as circles where
319+
two qubits overlap. In this drawing style, the interpretation the width
320+
and node_size parameters (provided in kwargs) determines the area of the
321+
circles, and line widths, respectively. For more information, see
322+
:func:`dwave_networkx.qubit_layout.draw_lineplot`.
323+
294324
kwargs : optional keywords
295325
See networkx.draw_networkx() for a description of optional keywords,
296326
with the exception of the `pos` parameter which is not used by this
@@ -327,6 +357,14 @@ def draw_chimera_yield(G, **kwargs):
327357
fault_style : string, optional (default='dashed')
328358
Edge fault line style (solid|dashed|dotted|dashdot)
329359
360+
line_plot : boolean (optional, default False)
361+
If line_plot is True, then qubits are drawn as line segments, and edges
362+
are drawn either as line segments between qubits, or as circles where
363+
two qubits overlap. In this drawing style, the interpretation the width
364+
and node_size parameters (provided in kwargs) determines the area of the
365+
circles, and line widths, respectively. For more information, see
366+
:func:`dwave_networkx.qubit_layout.draw_lineplot`.
367+
330368
kwargs : optional keywords
331369
See networkx.draw_networkx() for a description of optional keywords,
332370
with the exception of the `pos` parameter which is not used by this

‎dwave_networkx/drawing/pegasus_layout.py

+48-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import networkx as nx
2020
from networkx import draw
2121

22-
from dwave_networkx.drawing.qubit_layout import draw_qubit_graph, draw_embedding, draw_yield, normalize_size_and_aspect
22+
from dwave_networkx.drawing.qubit_layout import draw_qubit_graph, draw_embedding, draw_yield, normalize_size_and_aspect, draw_lineplot
2323
from dwave_networkx.generators.pegasus import pegasus_graph, pegasus_coordinates
2424
from dwave_networkx.drawing.chimera_layout import chimera_node_placer_2d
2525

@@ -133,6 +133,11 @@ def pegasus_node_placer_2d(G, scale=1., center=None, dim=2, crosses=False, norma
133133
"""
134134
import numpy as np
135135

136+
line_plot = False if normalize_kwargs is None else normalize_kwargs.get('line_plot')
137+
138+
if line_plot:
139+
crosses = False
140+
136141
m = G.graph['rows']
137142
h_offsets = G.graph["horizontal_offsets"]
138143
v_offsets = G.graph["vertical_offsets"]
@@ -180,7 +185,19 @@ def _xy_coords(u, w, k, z):
180185
# convention for Pegasus-lattice pictures is to invert the y-axis
181186
return np.hstack((xy * scale, paddims)) + center
182187

183-
return _xy_coords
188+
189+
if line_plot:
190+
qubit_dx = np.hstack(([5.75, 0], paddims)) * scale
191+
qubit_dy = np.hstack(([0, 5.75], paddims)) * scale
192+
def _line_coords(u, w, k, z):
193+
xy = _xy_coords(u, w, k, z)
194+
if u:
195+
return np.vstack((xy - qubit_dx, xy + qubit_dx))
196+
else:
197+
return np.vstack((xy - qubit_dy, xy + qubit_dy))
198+
return _line_coords
199+
else:
200+
return _xy_coords
184201

185202

186203
def draw_pegasus(G, crosses=False, **kwargs):
@@ -207,7 +224,16 @@ def draw_pegasus(G, crosses=False, **kwargs):
207224
crosses: boolean (optional, default False)
208225
If True, :math:`K_{4,4}` subgraphs are shown in a cross
209226
rather than L configuration. Ignored if G is defined with
210-
``nice_coordinates=True``.
227+
``nice_coordinates=True`` or ``line_plot=True``.
228+
229+
line_plot : boolean (optional, default False)
230+
If line_plot is True, then qubits are drawn as line segments, and edges
231+
are drawn either as line segments between qubits, or as circles where
232+
two qubits overlap. In this drawing style, the interpretation the width
233+
and node_size parameters (provided in kwargs) determines the area of the
234+
circles, and line widths, respectively. For more information, see
235+
:func:`dwave_networkx.qubit_layout.draw_lineplot`.
236+
211237
212238
kwargs : optional keywords
213239
See networkx.draw_networkx() for a description of optional keywords,
@@ -277,6 +303,15 @@ def draw_pegasus_embedding(G, *args, crosses=False, **kwargs):
277303
If True, chains in ``emb`` may overlap (contain the same vertices
278304
in G), and these overlaps are displayed as concentric circles.
279305
306+
line_plot : boolean (optional, default False)
307+
If line_plot is True, then qubits are drawn as line segments, and edges
308+
are drawn either as line segments between qubits, or as circles where
309+
two qubits overlap. In this drawing style, the interpretation the width
310+
and node_size parameters (provided in kwargs) determines the area of the
311+
circles, and line widths, respectively. For more information, see
312+
:func:`dwave_networkx.qubit_layout.draw_lineplot`.
313+
314+
280315
kwargs : optional keywords
281316
See networkx.draw_networkx() for a description of optional keywords,
282317
with the exception of the ``pos`` parameter, which is not used by this
@@ -315,7 +350,16 @@ def draw_pegasus_yield(G, crosses=False, **kwargs):
315350
crosses: boolean (optional, default False)
316351
If True, :math:`K_{4,4}` subgraphs are shown in a cross
317352
rather than L configuration. Ignored if G is defined with
318-
``nice_coordinates=True``.
353+
``nice_coordinates=True`` or ``line_plot=True``.
354+
355+
line_plot : boolean (optional, default False)
356+
If line_plot is True, then qubits are drawn as line segments, and edges
357+
are drawn either as line segments between qubits, or as circles where
358+
two qubits overlap. In this drawing style, the interpretation the width
359+
and node_size parameters (provided in kwargs) determines the area of the
360+
circles, and line widths, respectively. For more information, see
361+
:func:`dwave_networkx.qubit_layout.draw_lineplot`.
362+
319363
320364
kwargs : optional keywords
321365
See networkx.draw_networkx() for a description of optional keywords,

‎dwave_networkx/drawing/qubit_layout.py

+257-29
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@
2323
from networkx import draw
2424

2525
from dwave_networkx.drawing.distinguishable_colors import distinguishable_color_map
26+
from itertools import repeat, chain
27+
28+
from numbers import Number
2629

2730
__all__ = ['draw_qubit_graph']
2831

2932

3033
def draw_qubit_graph(G, layout, linear_biases={}, quadratic_biases={},
3134
nodelist=None, edgelist=None, cmap=None, edge_cmap=None, vmin=None, vmax=None,
32-
edge_vmin=None, edge_vmax=None, midpoint=None,
35+
edge_vmin=None, edge_vmax=None, midpoint=None, line_plot=False,
3336
**kwargs):
3437
"""Draws graph G according to layout.
3538
@@ -60,6 +63,16 @@ def draw_qubit_graph(G, layout, linear_biases={}, quadratic_biases={},
6063
be. If not provided, the colormap will default to the middle of
6164
min/max values provided.
6265
66+
line_plot : boolean (optional, default False)
67+
If line_plot is True, then qubits are drawn as line segments, and edges
68+
are drawn either as line segments between qubits, or as circles where
69+
two qubits overlap. In this drawing style, the interpretation the width
70+
and node_size parameters (provided in kwargs) determines the area of the
71+
circles, and line widths, respectively. Qubit line segments are given
72+
twice the width of edges. Layout should be a dict of the form
73+
{node: ((x0, y0), (y0, x0)), ...} -- instead of coordinates, the nodes
74+
are associated with endpoints of n-dimensional line segments.
75+
6376
kwargs : optional keywords
6477
See networkx.draw_networkx() for a description of optional keywords,
6578
with the exception of the `pos` parameter which is not used by this
@@ -178,15 +191,21 @@ def node_color(v):
178191
if ax is None:
179192
ax = fig.add_axes([0.01, 0.01, 0.98, 0.98])
180193

181-
draw(G, layout, ax=ax, nodelist=nodelist, edgelist=edgelist,
182-
cmap=cmap, edge_cmap=edge_cmap, vmin=vmin, vmax=vmax, edge_vmin=edge_vmin,
183-
edge_vmax=edge_vmax,
184-
**kwargs)
194+
if line_plot:
195+
draw_lineplot(G, layout, ax=ax, nodelist=nodelist, edgelist=edgelist,
196+
cmap=cmap, edge_cmap=edge_cmap, vmin=vmin, vmax=vmax, edge_vmin=edge_vmin,
197+
edge_vmax=edge_vmax,
198+
**kwargs)
199+
else:
200+
draw(G, layout, ax=ax, nodelist=nodelist, edgelist=edgelist,
201+
cmap=cmap, edge_cmap=edge_cmap, vmin=vmin, vmax=vmax, edge_vmin=edge_vmin,
202+
edge_vmax=edge_vmax,
203+
**kwargs)
185204

186205

187206
def draw_embedding(G, layout, emb, embedded_graph=None, interaction_edges=None,
188207
chain_color=None, unused_color=(0.9,0.9,0.9,1.0), cmap=None,
189-
show_labels=False, overlapped_embedding=False, **kwargs):
208+
show_labels=False, overlapped_embedding=False, line_plot=False, **kwargs):
190209
"""Draws an embedding onto the graph G, according to layout.
191210
192211
If interaction_edges is not None, then only display the couplers in that
@@ -240,6 +259,16 @@ def draw_embedding(G, layout, emb, embedded_graph=None, interaction_edges=None,
240259
the same vertices in G), and the drawing will display these overlaps as
241260
concentric circles.
242261
262+
line_plot : boolean (optional, default False)
263+
If line_plot is True, then qubits are drawn as line segments, and edges
264+
are drawn either as line segments between qubits, or as circles where
265+
two qubits overlap. In this drawing style, the interpretation the width
266+
and node_size parameters (provided in kwargs) determines the area of the
267+
circles, and line widths, respectively. Qubit line segments are given
268+
twice the width of edges. Layout should be a dict of the form
269+
{node: ((x0, y0), (y0, x0)), ...} -- instead of coordinates, the nodes
270+
are associated with endpoints of n-dimensional line segments.
271+
243272
kwargs : optional keywords
244273
See networkx.draw_networkx() for a description of optional keywords,
245274
with the exception of the `pos` parameter which is not used by this
@@ -252,6 +281,12 @@ def draw_embedding(G, layout, emb, embedded_graph=None, interaction_edges=None,
252281
except ImportError:
253282
raise ImportError("Matplotlib and numpy required for draw_chimera()")
254283

284+
if line_plot and show_labels:
285+
raise NotImplementedError("line_plot style drawings do not currently support node labels")
286+
287+
if line_plot and overlapped_embedding:
288+
raise NotImplementedError("line_plot style drawings do not currently support overlapped embeddings")
289+
255290
if nx.utils.is_string_like(unused_color):
256291
from matplotlib.colors import colorConverter
257292
alpha = kwargs.get('alpha', 1.0)
@@ -371,15 +406,25 @@ def show(p, q, u, v): return True
371406
c = emb[v]
372407
labels[list(c)[0]] = str(v)
373408

374-
# draw the background (unused) graph first
375-
if unused_color is not None:
376-
draw(G, layout, nodelist=nodelist, edgelist=background_edgelist,
377-
node_color=node_color, edge_color=background_edge_color,
378-
**kwargs)
409+
if line_plot:
410+
if unused_color is not None:
411+
draw_lineplot(G, layout, nodelist=nodelist, edgelist=background_edgelist,
412+
node_color=node_color, edge_color=background_edge_color,
413+
**kwargs)
379414

380-
draw(G, layout, nodelist=nodelist, edgelist=edgelist,
381-
node_color=node_color, edge_color=edge_color, labels=labels,
382-
**kwargs)
415+
draw_lineplot(G, layout, nodelist=nodelist, edgelist=edgelist,
416+
node_color=node_color, edge_color=edge_color, z_offset = 10,
417+
**kwargs)
418+
else:
419+
# draw the background (unused) graph first
420+
if unused_color is not None:
421+
draw(G, layout, nodelist=nodelist, edgelist=background_edgelist,
422+
node_color=node_color, edge_color=background_edge_color,
423+
**kwargs)
424+
425+
draw(G, layout, nodelist=nodelist, edgelist=edgelist,
426+
node_color=node_color, edge_color=edge_color, labels=labels,
427+
**kwargs)
383428

384429

385430
def compute_bags(C, emb):
@@ -426,7 +471,7 @@ def unoverlapped_embedding(G, emb, interaction_edges):
426471
def draw_yield(G, layout, perfect_graph, unused_color=(0.9,0.9,0.9,1.0),
427472
fault_color=(1.0,0.0,0.0,1.0), fault_shape='o',
428473
fault_style='dashed', incident_fault_color=(1.0,0.8,0.8,1.0),
429-
**kwargs):
474+
line_plot = False, **kwargs):
430475
"""Draws the given graph G with highlighted faults, according to layout.
431476
432477
Parameters
@@ -461,6 +506,16 @@ def draw_yield(G, layout, perfect_graph, unused_color=(0.9,0.9,0.9,1.0),
461506
fault_style : string, optional (default='dashed')
462507
Edge fault line style (solid|dashed|dotted,dashdot)
463508
509+
line_plot : boolean (optional, default False)
510+
If line_plot is True, then qubits are drawn as line segments, and edges
511+
are drawn either as line segments between qubits, or as circles where
512+
two qubits overlap. In this drawing style, the interpretation the width
513+
and node_size parameters (provided in kwargs) determines the area of the
514+
circles, and line widths, respectively. Qubit line segments are given
515+
twice the width of edges. Layout should be a dict of the form
516+
{node: ((x0, y0), (y0, x0)), ...} -- instead of coordinates, the nodes
517+
are associated with endpoints of n-dimensional line segments.
518+
464519
kwargs : optional keywords
465520
See networkx.draw_networkx() for a description of optional keywords,
466521
with the exception of the `pos` parameter which is not used by this
@@ -481,22 +536,36 @@ def draw_yield(G, layout, perfect_graph, unused_color=(0.9,0.9,0.9,1.0),
481536
incident_edgelist = edgeset(perfect_graph.edges) - edgeset(perfect_graph.subgraph(nodelist).edges)
482537
faults_edgelist = edgeset(perfect_graph.subgraph(nodelist).edges) - edgeset(G.edges)
483538

484-
faults_node_color = [fault_color for v in faults_nodelist]
485-
faults_edge_color = [fault_color for e in faults_edgelist]
486-
incident_edge_color = [incident_fault_color for e in incident_edgelist]
539+
if line_plot:
540+
if unused_color is not None:
541+
node_color = [fault_color if v in faults_nodelist else unused_color for v in perfect_graph]
542+
long_edgelist = list(edgeset(perfect_graph.edges) - incident_edgelist - faults_edgelist)
543+
else:
544+
node_color = []
545+
long_edgelist = []
546+
edge_color = [unused_color]*len(long_edgelist)
547+
long_edgelist.extend(incident_edgelist)
548+
edge_color.extend([incident_fault_color]*len(incident_edgelist))
549+
long_edgelist.extend(faults_edgelist)
550+
edge_color.extend([fault_color]*len(faults_edgelist))
551+
draw_lineplot(perfect_graph, layout, edgelist=long_edgelist, node_color=node_color, edge_color=edge_color, **kwargs)
552+
else:
553+
faults_node_color = [fault_color for v in faults_nodelist]
554+
faults_edge_color = [fault_color for e in faults_edgelist]
555+
incident_edge_color = [incident_fault_color for e in incident_edgelist]
487556

488-
# Draw edges first, in the order (unused, incident, faults)
489-
if unused_color is not None:
490-
unused_edge_color = [unused_color for e in G.edges()]
491-
nx.draw_networkx_edges(G, layout, edge_color=unused_edge_color, **kwargs)
492-
nx.draw_networkx_edges(perfect_graph, layout, incident_edgelist, style=fault_style, edge_color=incident_edge_color, **kwargs)
493-
nx.draw_networkx_edges(perfect_graph, layout, faults_edgelist, style=fault_style, edge_color=faults_edge_color, **kwargs)
557+
# Draw edges first, in the order (unused, incident, faults)
558+
if unused_color is not None:
559+
unused_edge_color = [unused_color for e in G.edges()]
560+
nx.draw_networkx_edges(G, layout, edge_color=unused_edge_color, **kwargs)
561+
nx.draw_networkx_edges(perfect_graph, layout, incident_edgelist, style=fault_style, edge_color=incident_edge_color, **kwargs)
562+
nx.draw_networkx_edges(perfect_graph, layout, faults_edgelist, style=fault_style, edge_color=faults_edge_color, **kwargs)
494563

495-
# Draw nodes second, in the order (unused, faults)
496-
if unused_color is not None:
497-
unused_node_color = [unused_color for e in G]
498-
nx.draw_networkx_nodes(G, layout, node_color = unused_node_color, **kwargs)
499-
nx.draw_networkx_nodes(perfect_graph, layout, faults_nodelist, node_shape=fault_shape, node_color=faults_node_color, **kwargs)
564+
# Draw nodes second, in the order (unused, faults)
565+
if unused_color is not None:
566+
unused_node_color = [unused_color for e in G]
567+
nx.draw_networkx_nodes(G, layout, node_color = unused_node_color, **kwargs)
568+
nx.draw_networkx_nodes(perfect_graph, layout, faults_nodelist, node_shape=fault_shape, node_color=faults_node_color, **kwargs)
500569

501570

502571
def normalize_size_and_aspect(scale, node_scale, kwargs):
@@ -532,3 +601,162 @@ def normalize_size_and_aspect(scale, node_scale, kwargs):
532601
else:
533602
kwargs['width'] = 2*(fig_scale / scale)
534603

604+
605+
def draw_lineplot(G, layout, *, ax, node_size, width, nodelist=None,
606+
edgelist=None, node_color='blue', edge_color='black',
607+
cmap=None, vmin=None, vmax=None, edge_cmap=None,
608+
edge_vmin=None, edge_vmax=None, z_offset=0):
609+
"""Draws the graph G with line segments representing nodes
610+
611+
This function is meant to be a drop-in replacement for :func:`networkx.draw`
612+
where nodes are associated with line segments (specified as 2x2 matrices
613+
[[x0, y0], [x1, y1]]). This function makes significant assumptions about
614+
the edges of the graph G, that hold when G is a Chimera, Pegasus, or Zephyr
615+
graph and the line segments are provided by :func:`chimera_layout`,
616+
:func:`pegasus_layout` and :func:`zephyr_layout` respectively. These graphs
617+
have three classes of edges:
618+
619+
* internal edges between qubits whose line segments are perpendicular
620+
and intersect at a point, which we draw with a circle located at the
621+
point of intersection,
622+
* external edges between qubits whose line segments are colinear, which
623+
we draw as a line segment between the nearest endpoints, and
624+
* odd edges between parallel qubits whose line segments are parallel and
625+
overlap in a perpendicular projection, which we draw as a line segment
626+
between the midpoints of the respective perpendicular projections
627+
628+
Parameters
629+
----------
630+
631+
G : networkx.Graph
632+
A graph constructed by :func:`chimera_layout`, :func:`pegasus_layout, or
633+
:func:`zephyr_layout`
634+
635+
pos : dict
636+
A dictionary with nodes as keys and 2x2 matrices [[x0, y0], [x1, y1]]
637+
representing the line segments of nodes.
638+
639+
ax : matplotlib.Axis
640+
The matplotlib Axis object to draw the graph on.
641+
642+
node_size : float
643+
The size (in area) of the circles used to depict internal edges.
644+
645+
width : float
646+
The width of line segments associated with edges, and half the width of
647+
line segments associated with nodes.
648+
649+
nodelist : iterable or None (default=None)
650+
The set of nodes to draw. If None, all nodes from G are drawn.
651+
652+
edgelist : iterable or None (default=None)
653+
The set of edges to draw. If both nodelist and edgelist are None, all
654+
edges of G are drawn. If edgelist is None, all edges from the subgraph
655+
``G.subgraph(nodelist)`` are drawn.
656+
657+
node_color : iterable or string (default='blue')
658+
The sequence of colors to use in drawing nodes of G. If node_color is
659+
not a string, the colors are taken in the same order as nodelist, and
660+
each color is either a float, a 3-tuple or 4-tuple of floats.
661+
662+
edge_color : iterable or string (default='black')
663+
The sequence of colors to use in drawing edges of G. If edge_color is
664+
not a string, the colors are taken in the same order as edgelist, and
665+
each color is either a float, a 3-tuple or 4-tuple of floats.
666+
667+
cmap : string or matplotlib.ColorMap or None (default=None)
668+
A colormap to color nodes with. Presumes that node_color is a sequence
669+
of floats.
670+
671+
vmin : float or None (default=None)
672+
Minimum value to use to use when normalizing node colors through cmap.
673+
674+
vmax : float or None (default=None)
675+
Maximum value to use to use when normalizing node colors through cmap.
676+
677+
edge_cmap : string or matplotlib.ColorMap or None (default=None)
678+
A colormap to color edges with. Presumes that edge_color is a sequence
679+
of floats.
680+
681+
edge_vmin : float or None (default=None)
682+
Minimum value to use to use when normalizing edge colors through
683+
edge_cmap
684+
685+
edge_vmax : float or None (default=None)
686+
Maximum value to use to use when normalizing edge colors through
687+
edge_cmap
688+
689+
z_offset : int (default=0)
690+
An offset to the zorder that various elements are drawn in. Edge lines
691+
are drawn with zorder=z_offset; horizontal node lines are drawn with
692+
zorder=zoffset+1; vertical node lines are drawn with zorder=zoffset+2,
693+
and edge circles are drawn with zorder=zoffset+3. This parameter can be
694+
used to layer line plots over or under eachother.
695+
"""
696+
697+
from networkx.drawing.nx_pylab import apply_alpha
698+
import numpy as np
699+
from matplotlib.collections import LineCollection, CircleCollection
700+
701+
if not isinstance(node_size, Number) or not isinstance(width, Number):
702+
raise NotImplementedError("Varying node size and edge width per element in line plots is not implemented")
703+
704+
if edgelist is None:
705+
if nodelist is not None:
706+
edgelist = G.subgraph(nodelist).edges
707+
else:
708+
edgelist = G.edges
709+
if nodelist is None:
710+
nodelist = G
711+
712+
node_color = apply_alpha(node_color, 1, nodelist, cmap=cmap, vmin=vmin, vmax=vmax)
713+
node_lines = np.array([layout[v] for v in nodelist], dtype='float')
714+
vertical = np.array([abs(x0-x1) < abs(y0-y1) for (x0, y0), (x1, y1) in node_lines], dtype='bool')
715+
if node_color.shape == (1, 4):
716+
vcolor = hcolor = node_color
717+
else:
718+
vcolor = node_color[vertical]
719+
hcolor = node_color[~vertical]
720+
ax.add_collection(LineCollection(node_lines[~vertical], edgecolor=hcolor, linewidths=width, zorder=1+z_offset, capstyle='round'))
721+
ax.add_collection(LineCollection(node_lines[vertical], edgecolor=vcolor, linewidths=width, zorder=2+z_offset, capstyle='round'))
722+
723+
if edge_color is not None and len(edgelist):
724+
vertical = dict(zip(nodelist, map(int, vertical)))
725+
as_line = np.full(len(edgelist), False, dtype='bool')
726+
edge_data = np.empty((len(edgelist), 2, 2), dtype='float')
727+
for i, (u, v) in enumerate(edgelist):
728+
u0, u1 = layout[u]
729+
v0, v1 = layout[v]
730+
orientation = vertical[u]
731+
if orientation == vertical[v]:
732+
as_line[i] = True
733+
if u0[orientation] > v1[orientation]:
734+
# external; v1 < u0
735+
edge_data[i] = v1, u0
736+
elif v0[orientation] > u1[orientation]:
737+
# external; u1 < v0
738+
edge_data[i] = u1, v0
739+
elif orientation:
740+
# odd, vertical
741+
ymean = (u0[1] + u1[1] + v0[1] + v1[1])/4
742+
edge_data[i] = (u0[0], ymean), (v0[0], ymean)
743+
else:
744+
# odd, horizontal
745+
xmean = (u0[0] + u1[0] + v0[0] + v1[0])/4
746+
edge_data[i] = (xmean, u0[1]), (xmean, v0[1])
747+
elif orientation:
748+
# internal, u is vertical and v is horizontal
749+
edge_data[i, 0] = u0[0], v0[1]
750+
else:
751+
# internal, v is vertical and u is horizontal
752+
edge_data[i, 0] = v0[0], u0[1]
753+
754+
edge_color = apply_alpha(edge_color, 1, edgelist, cmap=edge_cmap, vmin=edge_vmin, vmax=edge_vmax)
755+
if edge_color.shape == (1, 4):
756+
edge_line_color = edge_spot_color = edge_color
757+
else:
758+
edge_line_color = edge_color[as_line]
759+
edge_spot_color = edge_color[~as_line]
760+
ax.add_collection(LineCollection(edge_data[as_line], edgecolor=edge_line_color, linewidths=width/2, zorder=z_offset))
761+
ax.scatter(*edge_data[~as_line, 0].T, c=edge_spot_color, zorder=3+z_offset, s=node_size)
762+
ax.autoscale_view()

‎dwave_networkx/drawing/zephyr_layout.py

+45-9
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import networkx as nx
2020
from networkx import draw
2121

22-
from dwave_networkx.drawing.qubit_layout import draw_qubit_graph, draw_embedding, draw_yield, normalize_size_and_aspect
22+
from dwave_networkx.drawing.qubit_layout import draw_qubit_graph, draw_embedding, draw_yield, normalize_size_and_aspect, draw_lineplot
2323
from dwave_networkx.generators.zephyr import zephyr_graph, zephyr_coordinates
2424

2525

@@ -123,6 +123,8 @@ def zephyr_node_placer_2d(G, scale=1., center=None, dim=2, normalize_kwargs = No
123123
"""
124124
import numpy as np
125125

126+
line_plot = False if normalize_kwargs is None else normalize_kwargs.get('line_plot')
127+
126128
m = G.graph['rows']
127129
tile_width = 2*G.graph["tile"]
128130

@@ -157,7 +159,18 @@ def _xy_coords(u, w, k, j, z):
157159
if normalize_kwargs is not None:
158160
normalize_size_and_aspect(fabric_scale, 200, normalize_kwargs)
159161

160-
return _xy_coords
162+
if line_plot:
163+
qubit_dx = np.hstack(([tile_width - .25, 0], paddims)) * scale
164+
qubit_dy = np.hstack(([0, tile_width - .25], paddims)) * scale
165+
def _line_coords(u, w, k, j, z):
166+
xy = _xy_coords(u, w, k, j, z)
167+
if u:
168+
return np.vstack((xy - qubit_dx, xy + qubit_dx))
169+
else:
170+
return np.vstack((xy - qubit_dy, xy + qubit_dy))
171+
return _line_coords
172+
else:
173+
return _xy_coords
161174

162175
def draw_zephyr(G, **kwargs):
163176
"""Draws graph G in a Zephyr topology.
@@ -180,6 +193,14 @@ def draw_zephyr(G, **kwargs):
180193
edges in G and biases are numeric. Self-loop
181194
edges (i.e., :math:`i=j`) are treated as linear biases.
182195
196+
line_plot : boolean (optional, default False)
197+
If line_plot is True, then qubits are drawn as line segments, and edges
198+
are drawn either as line segments between qubits, or as circles where
199+
two qubits overlap. In this drawing style, the interpretation the width
200+
and node_size parameters (provided in kwargs) determines the area of the
201+
circles, and line widths, respectively. For more information, see
202+
:func:`dwave_networkx.qubit_layout.draw_lineplot`.
203+
183204
kwargs : optional keywords
184205
See networkx.draw_networkx() for a description of optional keywords,
185206
with the exception of the ``pos`` parameter, which is not used by this
@@ -198,9 +219,8 @@ def draw_zephyr(G, **kwargs):
198219
>>> plt.show() # doctest: +SKIP
199220
200221
"""
201-
202-
draw_qubit_graph(G, zephyr_layout(G), **kwargs)
203-
222+
layout = zephyr_layout(G, normalize_kwargs = kwargs)
223+
draw_qubit_graph(G, layout, **kwargs)
204224

205225
def draw_zephyr_embedding(G, *args, **kwargs):
206226
"""Draws an embedding onto Zephyr graph G.
@@ -244,13 +264,22 @@ def draw_zephyr_embedding(G, *args, **kwargs):
244264
If True, chains in ``emb`` may overlap (contain the same vertices
245265
in G), and these overlaps are displayed as concentric circles.
246266
267+
line_plot : boolean (optional, default False)
268+
If line_plot is True, then qubits are drawn as line segments, and edges
269+
are drawn either as line segments between qubits, or as circles where
270+
two qubits overlap. In this drawing style, the interpretation the width
271+
and node_size parameters (provided in kwargs) determines the area of the
272+
circles, and line widths, respectively. For more information, see
273+
:func:`dwave_networkx.qubit_layout.draw_lineplot`.
274+
247275
kwargs : optional keywords
248276
See networkx.draw_networkx() for a description of optional keywords,
249277
with the exception of the ``pos`` parameter, which is not used by this
250278
function. If ``linear_biases`` or ``quadratic_biases`` are provided,
251279
any provided ``node_color`` or ``edge_color`` arguments are ignored.
252280
"""
253-
draw_embedding(G, zephyr_layout(G), *args, **kwargs)
281+
layout = zephyr_layout(G, normalize_kwargs = kwargs)
282+
draw_embedding(G, layout, *args, **kwargs)
254283

255284
def draw_zephyr_yield(G, **kwargs):
256285
"""Draws the given graph G with highlighted faults, according to layout.
@@ -278,6 +307,14 @@ def draw_zephyr_yield(G, **kwargs):
278307
fault_style : string, optional (default='dashed')
279308
Edge fault line style (solid|dashed|dotted|dashdot)
280309
310+
line_plot : boolean (optional, default False)
311+
If line_plot is True, then qubits are drawn as line segments, and edges
312+
are drawn either as line segments between qubits, or as circles where
313+
two qubits overlap. In this drawing style, the interpretation the width
314+
and node_size parameters (provided in kwargs) determines the area of the
315+
circles, and line widths, respectively. For more information, see
316+
:func:`dwave_networkx.qubit_layout.draw_lineplot`.
317+
281318
kwargs : optional keywords
282319
See networkx.draw_networkx() for a description of optional keywords,
283320
with the exception of the `pos` parameter which is not used by this
@@ -293,7 +330,6 @@ def draw_zephyr_yield(G, **kwargs):
293330
raise ValueError("Target zephyr graph needs to have columns, rows, \
294331
tile, and label attributes to be able to identify faulty qubits.")
295332

296-
297333
perfect_graph = zephyr_graph(m, t, coordinates=coordinates)
298-
299-
draw_yield(G, zephyr_layout(perfect_graph), perfect_graph, **kwargs)
334+
layout = zephyr_layout(perfect_graph, normalize_kwargs = kwargs)
335+
draw_yield(G, layout, perfect_graph, **kwargs)

0 commit comments

Comments
 (0)
Please sign in to comment.