From 922e011df6147bab352e82e1831de6752acb989f Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 24 Mar 2022 13:10:49 -0400 Subject: [PATCH 01/14] Make plot_gate_map generic for any backend Previously the plot_gate_map() family of visualization functions only worked with a fixed hard coded list of number of qubits. The function made the assumption if the backend had a matching number of qubits it was an IBM backend that had the same number of qubits (and fell into a fixed connectivity topology) and would use the manually set graph layout to visualize the function. This was problematic because it didn't actually work for any backend, and if a backend had the same number of qubits as an IBM backend but a different connectivity it would use a potentially nonoptimal layout. This also required us to update the hard coded list whenever a new IBM backend was released to support visualizing them. Additionally the functions only worked with BackendV1 and BaseBackend based backends and would fail if a BackendV2 object were passed to the function. This commit address these issues by making 4 key changes to the function, first it adds a new qubit_coordinates kwarg to all the methods which if specifed can be used to manually specify a graph layout to use for the visualization. The second is that the hardcoded list for layouts are only used if it's an IBM backend (or a fake backend) so that we're using them where appropriate (the 127q layout for ibm_washington is also added). The third change is that if a graph layout isn't provided and it's not an ibm backend with a hardcoded entry, retworkx's spring_layout() function is used to create a layout. In the future we can use a more appropriate planar_layout() function after its available in retworkx (see https://github.com/Qiskit/retworkx/issues/438 ). Finally the last change is that BackendV2 support is added to the function to make sure we're able to use the visualization function regardless of what type of backend is passed in. --- qiskit/visualization/gate_map.py | 311 ++++++++++++++---- ...update-plot-gate-map-9ed6ad5490bafbbf.yaml | 26 ++ .../references/16_plot_circuit_layout.png | Bin 11911 -> 13323 bytes 3 files changed, 282 insertions(+), 55 deletions(-) create mode 100644 releasenotes/notes/update-plot-gate-map-9ed6ad5490bafbbf.yaml diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 5e6aea5c3ba8..27471dfc3b43 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -14,8 +14,12 @@ import math from typing import List + import numpy as np +import retworkx as rx + from qiskit.exceptions import QiskitError +from qiskit.utils.backend_utils import _get_backend_interface_version from qiskit.utils import optionals as _optionals from .exceptions import VisualizationError from .utils import matplotlib_close_if_inline @@ -36,6 +40,7 @@ def plot_gate_map( font_color="w", ax=None, filename=None, + qubit_coordinates=None, ): """Plots the gate map of a device. @@ -54,6 +59,9 @@ def plot_gate_map( font_color (str): The font color for the qubit labels. ax (Axes): A Matplotlib axes instance. filename (str): file path to save image to. + qubit_coordinates (Sequence): A sequence type (list or array being the + most common) of 2d coordinates for each qubit. The length of the + sequence much mast the number of qubits on the backend. Returns: Figure: A Matplotlib figure instance. @@ -82,9 +90,6 @@ def plot_gate_map( backend = accountProvider.get_backend('ibmq_vigo') plot_gate_map(backend) """ - if backend.configuration().simulator: - raise QiskitError("Requires a device backend, not simulator.") - qubit_coordinates_map = {} qubit_coordinates_map[1] = [[0, 0]] @@ -338,14 +343,178 @@ def plot_gate_map( [8, 10], ] - config = backend.configuration() - num_qubits = config.n_qubits - coupling_map = config.coupling_map - qubit_coordinates = qubit_coordinates_map.get(num_qubits) + qubit_coordinates_map[127] = [ + [1, 1], + [2, 1], + [3, 1], + [4, 1], + [5, 1], + [6, 1], + [7, 1], + [8, 1], + [9, 1], + [10, 1], + [11, 1], + [12, 1], + [13, 1], + [14, 1], + [1, 2], + [5, 2], + [9, 2], + [13, 2], + [1, 3], + [2, 3], + [3, 3], + [4, 3], + [5, 3], + [6, 3], + [7, 3], + [8, 3], + [9, 3], + [10, 3], + [11, 3], + [12, 3], + [13, 3], + [14, 3], + [15, 3], + [3, 4], + [7, 4], + [11, 4], + [15, 4], + [1, 5], + [2, 5], + [3, 5], + [4, 5], + [5, 5], + [6, 5], + [7, 5], + [8, 5], + [9, 5], + [10, 5], + [11, 5], + [12, 5], + [13, 5], + [14, 5], + [15, 5], + [1, 6], + [5, 6], + [9, 6], + [13, 6], + [1, 7], + [2, 7], + [3, 7], + [4, 7], + [5, 7], + [6, 7], + [7, 7], + [8, 7], + [9, 7], + [10, 7], + [11, 7], + [12, 7], + [13, 7], + [14, 7], + [15, 7], + [3, 8], + [7, 8], + [11, 8], + [15, 8], + [1, 9], + [2, 9], + [3, 9], + [4, 9], + [5, 9], + [6, 9], + [7, 9], + [8, 9], + [9, 9], + [10, 9], + [11, 9], + [12, 9], + [13, 9], + [14, 9], + [15, 9], + [1, 10], + [5, 10], + [9, 10], + [13, 10], + [1, 11], + [2, 11], + [3, 11], + [4, 11], + [5, 11], + [6, 11], + [7, 11], + [8, 11], + [9, 11], + [10, 11], + [11, 11], + [12, 11], + [13, 11], + [14, 11], + [15, 11], + [3, 12], + [7, 12], + [11, 12], + [15, 12], + [2, 13], + [3, 13], + [4, 13], + [5, 13], + [6, 13], + [7, 13], + [8, 13], + [9, 13], + [10, 13], + [11, 13], + [12, 13], + [13, 13], + [14, 13], + [15, 13], + ] + backend_version = _get_backend_interface_version(backend) + if backend_version <= 1: + from qiskit.transpiler.coupling import CouplingMap + + if backend.configuration().simulator: + raise QiskitError("Requires a device backend, not simulator.") + config = backend.configuration() + num_qubits = config.n_qubits + coupling_map = CouplingMap(config.coupling_map) + name = backend.name() + else: + num_qubits = backend.num_qubits + coupling_map = backend.coupling_map + name = backend.name + if "ibm" in name or "fake" in name: + qubit_coordinates = qubit_coordinates_map.get(num_qubits, None) + + if qubit_coordinates is None: + qubit_coordinates_rx = rx.spring_layout(coupling_map.graph, k=1.0, seed=1234) + print(qubit_coordinates_rx) + qubit_coordinates = [ + (int(10 * qubit_coordinates_rx[i][0]), int(10 * qubit_coordinates_rx[i][1])) + for i in range(num_qubits) + ] + + if any(x[0] < 1 or x[1] < 1 for x in qubit_coordinates): + min_entry = min(qubit_coordinates, key=lambda x: min(x[0], x[1])) + negative_offset = 0 - min(min_entry) + qubit_coordinates = [ + (x[0] + negative_offset, x[1] + negative_offset) for x in qubit_coordinates + ] + print(qubit_coordinates) + + if len(qubit_coordinates) != num_qubits: + raise QiskitError( + f"The number of specified qubit coordinates {len(qubit_coordinates)} " + f"does not match the device number of qubits: {num_qubits}" + ) + return plot_coupling_map( num_qubits, qubit_coordinates, - coupling_map, + coupling_map.get_edges(), figsize, plot_directed, label_qubits, @@ -614,7 +783,13 @@ def plot_circuit_layout(circuit, backend, view="virtual"): if circuit._layout is None: raise QiskitError("Circuit has no layout. Perhaps it has not been transpiled.") - num_qubits = backend.configuration().n_qubits + backend_version = _get_backend_interface_version(backend) + if backend_version <= 1: + num_qubits = backend.configuration().n_qubits + cmap = backend.configuration().coupling_map + else: + num_qubits = backend.num_qubits + cmap = backend.coupling_map qubits = [] qubit_labels = [None] * num_qubits @@ -649,8 +824,6 @@ def plot_circuit_layout(circuit, backend, view="virtual"): for k in qubits: qcolors[k] = "k" - cmap = backend.configuration().coupling_map - lcolors = ["#648fff"] * len(cmap) for idx, edge in enumerate(cmap): @@ -705,24 +878,78 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True): color_map = sns.cubehelix_palette(reverse=True, as_cmap=True) - props = backend.properties().to_dict() - config = backend.configuration().to_dict() - - num_qubits = config["n_qubits"] - - # sx error rates - single_gate_errors = [0] * num_qubits - for gate in props["gates"]: - if gate["gate"] == "sx": - _qubit = gate["qubits"][0] - for param in gate["parameters"]: - if param["name"] == "gate_error": - single_gate_errors[_qubit] = param["value"] - break - else: - raise VisualizationError( - f"Backend '{backend}' did not supply an error for the 'sx' gate." - ) + backend_version = _get_backend_interface_version(backend) + if backend_version <= 1: + num_qubits = backend.configuration().n_qubits + cmap = backend.configuration().coupling_map + props = backend.properties().to_dict() + single_gate_errors = [0] * num_qubits + read_err = [0] * num_qubits + cx_errors = [] + # sx error rates + for gate in props["gates"]: + if gate["gate"] == "sx": + _qubit = gate["qubits"][0] + for param in gate["parameters"]: + if param["name"] == "gate_error": + single_gate_errors[_qubit] = param["value"] + break + else: + raise VisualizationError( + f"Backend '{backend}' did not supply an error for the 'sx' gate." + ) + if cmap: + directed = False + if num_qubits < 20: + for edge in cmap: + if not [edge[1], edge[0]] in cmap: + directed = True + break + + for line in cmap: + for item in props["gates"]: + if item["qubits"] == line: + cx_errors.append(item["parameters"][0]["value"]) + break + else: + continue + for qubit in range(num_qubits): + for item in props["qubits"][qubit]: + if item["name"] == "readout_error": + read_err.append(item["value"]) + + else: + num_qubits = backend.num_qubits + cmap = backend.coupling_map + two_q_error_map = {} + single_gate_errors = [0] * num_qubits + read_err = [0] * num_qubits + cx_errors = [] + for gate, prop_dict in backend.target.items(): + if prop_dict is None or None in prop_dict: + continue + for qargs, inst_props in prop_dict.items(): + if gate == "measure": + if inst_props.error is not None: + read_err[qargs[0]] = inst_props.error + elif len(qargs) == 1: + if inst_props.error is not None: + single_gate_errors = max(single_gate_errors[qargs[0]], inst_props.error) + elif len(qargs) == 2: + if inst_props.error is not None: + two_q_error_map[qargs] = max( + two_q_error_map.get(qargs, 0), inst_props.error + ) + if cmap: + directed = False + if num_qubits < 20: + for edge in cmap: + if not [edge[1], edge[0]] in cmap: + directed = True + break + for line in cmap: + err = two_q_error_map.get(tuple(line), 0) + cx_errors.append(err) # Convert to percent single_gate_errors = 100 * np.asarray(single_gate_errors) @@ -733,26 +960,9 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True): ) q_colors = [color_map(single_norm(err)) for err in single_gate_errors] - cmap = config["coupling_map"] - directed = False line_colors = [] if cmap: - directed = False - if num_qubits < 20: - for edge in cmap: - if not [edge[1], edge[0]] in cmap: - directed = True - break - - cx_errors = [] - for line in cmap: - for item in props["gates"]: - if item["qubits"] == line: - cx_errors.append(item["parameters"][0]["value"]) - break - else: - continue # Convert to percent cx_errors = 100 * np.asarray(cx_errors) @@ -761,15 +971,6 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True): cx_norm = matplotlib.colors.Normalize(vmin=min(cx_errors), vmax=max(cx_errors)) line_colors = [color_map(cx_norm(err)) for err in cx_errors] - # Measurement errors - - read_err = [] - - for qubit in range(num_qubits): - for item in props["qubits"][qubit]: - if item["name"] == "readout_error": - read_err.append(item["value"]) - read_err = 100 * np.asarray(read_err) avg_read_err = np.mean(read_err) max_read_err = np.max(read_err) diff --git a/releasenotes/notes/update-plot-gate-map-9ed6ad5490bafbbf.yaml b/releasenotes/notes/update-plot-gate-map-9ed6ad5490bafbbf.yaml new file mode 100644 index 000000000000..981e04a9be9f --- /dev/null +++ b/releasenotes/notes/update-plot-gate-map-9ed6ad5490bafbbf.yaml @@ -0,0 +1,26 @@ +--- +features: + - | + The :func:`~.plot_gate_map` visualization function and the functions built + on top of it, :func:`~.plot_error_map` and :func:`~.plot_circuit_layout`, + have a new keyword argument, ``qubit_coordinates``. This argument takes + a sequence of 2D coordinates to use for plotting each qubit in the backend + being visualized. If specified this sequence must have a length equal to + the number of qubits on the backend and it will be used instead of the + default behavior. + - | + The :func:`~.plot_gate_map` visualization function and the functions built + on top of it, :func:`~.plot_error_map` and :func:`~.plot_circuit_layout`, + now are able to plot any backend not just those with the number of qubits + equal to one of the IBM backends. This relies on + the retworkx ``spring_layout()`` + `function `__ + to generate the layout for the visualization. If the default layout doesn't + work with a backend's particular coupling graph you can use the + ``qubit_coordinates`` function to set a custom layout. + - | + The :func:`~.plot_gate_map` visualization function and the functions built + on top of it, :func:`~.plot_error_map` and :func:`~.plot_circuit_layout`, + are now able to function with a :class:`~.BackendV2` based backend. + Previously, these functions only worked with :class:`~.BaseBackend` or + :class:`~.BackendV1` based backends. diff --git a/test/python/visualization/references/16_plot_circuit_layout.png b/test/python/visualization/references/16_plot_circuit_layout.png index 72aaf3b3f5b994560b12e113f46f4aad0417651e..7f6c34d19c17326e1571a26e2d6dd20ddc985839 100644 GIT binary patch literal 13323 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 Uts)2DKkndZs%fj1J+h4WFLn4K@&Et; literal 11911 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 Date: Thu, 24 Mar 2022 14:15:18 -0400 Subject: [PATCH 02/14] Add missing kwargs --- qiskit/visualization/gate_map.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 27471dfc3b43..764a7280219a 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -61,7 +61,9 @@ def plot_gate_map( filename (str): file path to save image to. qubit_coordinates (Sequence): A sequence type (list or array being the most common) of 2d coordinates for each qubit. The length of the - sequence much mast the number of qubits on the backend. + sequence much mast the number of qubits on the backend. The sequence + should be the planar coordinates in a 0-based square grid where each + qubit is located. Returns: Figure: A Matplotlib figure instance. @@ -734,7 +736,7 @@ def plot_coupling_map( return None -def plot_circuit_layout(circuit, backend, view="virtual"): +def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None): """Plot the layout of a circuit transpiled for a given target backend. @@ -742,6 +744,11 @@ def plot_circuit_layout(circuit, backend, view="virtual"): circuit (QuantumCircuit): Input quantum circuit. backend (BaseBackend): Target backend. view (str): Layout view: either 'virtual' or 'physical'. + qubit_coordinates (Sequence): A sequence type (list or array being the + most common) of 2d coordinates for each qubit. The length of the + sequence much mast the number of qubits on the backend. The sequence + should be the planar coordinates in a 0-based square grid where each + qubit is located. Returns: Figure: A matplotlib figure showing layout. @@ -830,19 +837,30 @@ def plot_circuit_layout(circuit, backend, view="virtual"): if edge[0] in qubits and edge[1] in qubits: lcolors[idx] = "k" - fig = plot_gate_map(backend, qubit_color=qcolors, qubit_labels=qubit_labels, line_color=lcolors) + fig = plot_gate_map( + backend, + qubit_color=qcolors, + qubit_labels=qubit_labels, + line_color=lcolors, + qubit_coordinates=qubit_coordinates, + ) return fig @_optionals.HAS_MATPLOTLIB.require_in_call @_optionals.HAS_SEABORN.require_in_call -def plot_error_map(backend, figsize=(12, 9), show_title=True): +def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates=None): """Plots the error map of a given backend. Args: backend (IBMQBackend): Given backend. figsize (tuple): Figure size in inches. show_title (bool): Show the title or not. + qubit_coordinates (Sequence): A sequence type (list or array being the + most common) of 2d coordinates for each qubit. The length of the + sequence much mast the number of qubits on the backend. The sequence + should be the planar coordinates in a 0-based square grid where each + qubit is located. Returns: Figure: A matplotlib figure showing error map. @@ -1000,6 +1018,7 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True): line_width=5, plot_directed=directed, ax=main_ax, + qubit_coordinates=qubit_coordinates, ) main_ax.axis("off") main_ax.set_aspect(1) From ed03e36e9ce1dccbd5a7b82be1ecdbf2e702d59d Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 24 Mar 2022 14:18:07 -0400 Subject: [PATCH 03/14] Fix import cycle --- qiskit/transpiler/passmanager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qiskit/transpiler/passmanager.py b/qiskit/transpiler/passmanager.py index b472d463c808..a206af3cf0ab 100644 --- a/qiskit/transpiler/passmanager.py +++ b/qiskit/transpiler/passmanager.py @@ -16,7 +16,6 @@ import dill -from qiskit.visualization import pass_manager_drawer from qiskit.tools.parallel import parallel_map from qiskit.circuit import QuantumCircuit from .basepasses import BasePass @@ -301,6 +300,8 @@ def draw(self, filename=None, style=None, raw=False): Raises: ImportError: when nxpd or pydot not installed. """ + from qiskit.visualization import pass_manager_drawer + return pass_manager_drawer(self, filename=filename, style=style, raw=raw) def passes(self) -> List[Dict[str, BasePass]]: From 82e9182fa4594e97ffd3d838c0f941c835ff788a Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 24 Mar 2022 14:18:17 -0400 Subject: [PATCH 04/14] Remove stray debug prints --- qiskit/visualization/gate_map.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 764a7280219a..2abb9a95eb5b 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -493,7 +493,6 @@ def plot_gate_map( if qubit_coordinates is None: qubit_coordinates_rx = rx.spring_layout(coupling_map.graph, k=1.0, seed=1234) - print(qubit_coordinates_rx) qubit_coordinates = [ (int(10 * qubit_coordinates_rx[i][0]), int(10 * qubit_coordinates_rx[i][1])) for i in range(num_qubits) @@ -505,7 +504,6 @@ def plot_gate_map( qubit_coordinates = [ (x[0] + negative_offset, x[1] + negative_offset) for x in qubit_coordinates ] - print(qubit_coordinates) if len(qubit_coordinates) != num_qubits: raise QiskitError( From 80a33e44b9e79b1da8f9345aff6b1e1680b7ddfd Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 24 Mar 2022 14:23:08 -0400 Subject: [PATCH 05/14] Inline function causing import cycle --- qiskit/visualization/gate_map.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 2abb9a95eb5b..b1da48c48c64 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -19,12 +19,20 @@ import retworkx as rx from qiskit.exceptions import QiskitError -from qiskit.utils.backend_utils import _get_backend_interface_version from qiskit.utils import optionals as _optionals from .exceptions import VisualizationError from .utils import matplotlib_close_if_inline +def _get_backend_interface_version(backend): + backend_interface_version = getattr(backend, "version", None) + # Handle deprecated BaseBackend based backends which have a version() + # method + if not isinstance(backend_interface_version, int): + backend_interface_version = 0 + return backend_interface_version + + @_optionals.HAS_MATPLOTLIB.require_in_call def plot_gate_map( backend, From 8a39d86e0882f6a6ae628f22479a470054f2eb83 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 24 Mar 2022 14:37:10 -0400 Subject: [PATCH 06/14] Update 127q ibm layout to be consistent with others --- qiskit/visualization/gate_map.py | 231 ++++++++++++++++--------------- 1 file changed, 116 insertions(+), 115 deletions(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index b1da48c48c64..0ecc868a60c6 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -354,134 +354,135 @@ def plot_gate_map( ] qubit_coordinates_map[127] = [ - [1, 1], + [0, 0], + [0, 1], + [0, 2], + [0, 3], + [0, 4], + [0, 5], + [0, 6], + [0, 7], + [0, 8], + [0, 9], + [0, 10], + [0, 11], + [0, 12], + [0, 13], + [1, 0], + [1, 4], + [1, 8], + [1, 12], + [2, 0], [2, 1], - [3, 1], - [4, 1], - [5, 1], - [6, 1], - [7, 1], - [8, 1], - [9, 1], - [10, 1], - [11, 1], - [12, 1], - [13, 1], - [14, 1], - [1, 2], - [5, 2], - [9, 2], - [13, 2], - [1, 3], + [2, 2], [2, 3], - [3, 3], - [4, 3], - [5, 3], - [6, 3], - [7, 3], - [8, 3], - [9, 3], - [10, 3], - [11, 3], - [12, 3], - [13, 3], - [14, 3], - [15, 3], - [3, 4], - [7, 4], - [11, 4], - [15, 4], - [1, 5], + [2, 4], [2, 5], - [3, 5], - [4, 5], - [5, 5], - [6, 5], - [7, 5], - [8, 5], - [9, 5], - [10, 5], - [11, 5], - [12, 5], - [13, 5], - [14, 5], - [15, 5], - [1, 6], - [5, 6], - [9, 6], - [13, 6], - [1, 7], + [2, 6], [2, 7], - [3, 7], - [4, 7], - [5, 7], - [6, 7], - [7, 7], - [8, 7], - [9, 7], - [10, 7], - [11, 7], - [12, 7], - [13, 7], - [14, 7], - [15, 7], - [3, 8], - [7, 8], - [11, 8], - [15, 8], - [1, 9], + [2, 8], [2, 9], - [3, 9], - [4, 9], - [5, 9], - [6, 9], - [7, 9], - [8, 9], - [9, 9], - [10, 9], - [11, 9], - [12, 9], - [13, 9], - [14, 9], - [15, 9], - [1, 10], - [5, 10], - [9, 10], - [13, 10], - [1, 11], + [2, 10], [2, 11], - [3, 11], - [4, 11], - [5, 11], - [6, 11], - [7, 11], - [8, 11], - [9, 11], - [10, 11], - [11, 11], - [12, 11], - [13, 11], - [14, 11], - [15, 11], - [3, 12], - [7, 12], - [11, 12], - [15, 12], + [2, 12], [2, 13], - [3, 13], + [2, 14], + [3, 2], + [3, 6], + [3, 10], + [3, 14], + [4, 0], + [4, 1], + [4, 2], + [4, 3], + [4, 4], + [4, 5], + [4, 6], + [4, 7], + [4, 8], + [4, 9], + [4, 10], + [4, 11], + [4, 12], [4, 13], - [5, 13], + [4, 14], + [5, 0], + [5, 4], + [5, 8], + [5, 12], + [6, 0], + [6, 1], + [6, 2], + [6, 3], + [6, 4], + [6, 5], + [6, 6], + [6, 7], + [6, 8], + [6, 9], + [6, 10], + [6, 11], + [6, 12], [6, 13], - [7, 13], + [6, 14], + [7, 2], + [7, 6], + [7, 10], + [7, 14], + [8, 0], + [8, 1], + [8, 2], + [8, 3], + [8, 4], + [8, 5], + [8, 6], + [8, 7], + [8, 8], + [8, 9], + [8, 10], + [8, 11], + [8, 12], [8, 13], - [9, 13], + [8, 14], + [9, 0], + [9, 4], + [9, 8], + [9, 12], + [10, 0], + [10, 1], + [10, 2], + [10, 3], + [10, 4], + [10, 5], + [10, 6], + [10, 7], + [10, 8], + [10, 9], + [10, 10], + [10, 11], + [10, 12], [10, 13], - [11, 13], + [10, 14], + [11, 2], + [11, 6], + [11, 10], + [11, 14], + [12, 1], + [12, 2], + [12, 3], + [12, 4], + [12, 5], + [12, 6], + [12, 7], + [12, 8], + [12, 9], + [12, 10], + [12, 11], + [12, 12], [12, 13], - [13, 13], - [14, 13], - [15, 13], + [12, 14], ] + backend_version = _get_backend_interface_version(backend) if backend_version <= 1: from qiskit.transpiler.coupling import CouplingMap From 4d7793e70337fa10e4233af50054f5cb517a6b60 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 24 Mar 2022 16:55:50 -0400 Subject: [PATCH 07/14] Tweak spring_layout() usage and coordinate system conversion --- qiskit/visualization/gate_map.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 0ecc868a60c6..c26adc1e2880 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -501,9 +501,11 @@ def plot_gate_map( qubit_coordinates = qubit_coordinates_map.get(num_qubits, None) if qubit_coordinates is None: - qubit_coordinates_rx = rx.spring_layout(coupling_map.graph, k=1.0, seed=1234) + # Replace with planar_layout() when retworkx offers it + qubit_coordinates_rx = rx.spring_layout(coupling_map.graph, seed=1234) + scaling_factor = 10**int(math.log10(num_qubits) + 1) qubit_coordinates = [ - (int(10 * qubit_coordinates_rx[i][0]), int(10 * qubit_coordinates_rx[i][1])) + (int(scaling_factor * qubit_coordinates_rx[i][0]), int(scaling_factor * qubit_coordinates_rx[i][1])) for i in range(num_qubits) ] From dd3c82c8d43a42bd8d090bdc9c91f06b783008fd Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 24 Mar 2022 18:07:55 -0400 Subject: [PATCH 08/14] Fix lint --- qiskit/visualization/gate_map.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index c26adc1e2880..c24b15f86d64 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -503,9 +503,12 @@ def plot_gate_map( if qubit_coordinates is None: # Replace with planar_layout() when retworkx offers it qubit_coordinates_rx = rx.spring_layout(coupling_map.graph, seed=1234) - scaling_factor = 10**int(math.log10(num_qubits) + 1) + scaling_factor = 10 ** int(math.log10(num_qubits) + 1) qubit_coordinates = [ - (int(scaling_factor * qubit_coordinates_rx[i][0]), int(scaling_factor * qubit_coordinates_rx[i][1])) + ( + int(scaling_factor * qubit_coordinates_rx[i][0]), + int(scaling_factor * qubit_coordinates_rx[i][1]), + ) for i in range(num_qubits) ] From 2417e1c03d352cc2b70b137c1478404457f17355 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 30 Mar 2022 13:37:22 -0400 Subject: [PATCH 09/14] Only use hardcoded layouts if qubit_coordinates is not set This commit updates the logic around when we use the default hard coded graph layouts for ibm backends. Previously there was a bug in the if condition which would cause the hard coded layout to be unconditionally used for an ibm backend, even if the user specified their own layout. This fixes this by only using the hard coded layouts when qubit_coordinates is not set. --- qiskit/visualization/gate_map.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index c24b15f86d64..9aa2d4f8f744 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -497,7 +497,7 @@ def plot_gate_map( num_qubits = backend.num_qubits coupling_map = backend.coupling_map name = backend.name - if "ibm" in name or "fake" in name: + if qubit_coordinates is None and ("ibm" in name or "fake" in name): qubit_coordinates = qubit_coordinates_map.get(num_qubits, None) if qubit_coordinates is None: From 59ee85ab5130206963ad6e76b84f7f1539f10ccc Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 30 Mar 2022 18:00:46 -0400 Subject: [PATCH 10/14] Apply suggestions from code review Co-authored-by: Kevin Hartman --- qiskit/visualization/gate_map.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 9aa2d4f8f744..805cf275189c 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -69,7 +69,7 @@ def plot_gate_map( filename (str): file path to save image to. qubit_coordinates (Sequence): A sequence type (list or array being the most common) of 2d coordinates for each qubit. The length of the - sequence much mast the number of qubits on the backend. The sequence + sequence much match the number of qubits on the backend. The sequence should be the planar coordinates in a 0-based square grid where each qubit is located. @@ -964,7 +964,7 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates= read_err[qargs[0]] = inst_props.error elif len(qargs) == 1: if inst_props.error is not None: - single_gate_errors = max(single_gate_errors[qargs[0]], inst_props.error) + single_gate_errors[qargs[0]] = max(single_gate_errors[qargs[0]], inst_props.error) elif len(qargs) == 2: if inst_props.error is not None: two_q_error_map[qargs] = max( From 01fb3a03e30f59338c032f085ceb8314ed7a4ea8 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 30 Mar 2022 18:01:58 -0400 Subject: [PATCH 11/14] Fix negative value offset correction in plot_gate_map() Previously the offset detection was looking for values < 0 which was needlessly triggering an offset loop for values >=0 and < 1. We only need to correct for negative values if there are any negative values in the 2d coordinates being used for the graph layout. --- qiskit/visualization/gate_map.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 805cf275189c..ef9b18c9cee7 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -512,7 +512,7 @@ def plot_gate_map( for i in range(num_qubits) ] - if any(x[0] < 1 or x[1] < 1 for x in qubit_coordinates): + if any(x[0] < 0 or x[1] < 0 for x in qubit_coordinates): min_entry = min(qubit_coordinates, key=lambda x: min(x[0], x[1])) negative_offset = 0 - min(min_entry) qubit_coordinates = [ From fac6ffb169a60e147e0340c68c2f570fdcffcc1b Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 30 Mar 2022 18:03:57 -0400 Subject: [PATCH 12/14] Remove unnecessary continue --- qiskit/visualization/gate_map.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index ef9b18c9cee7..56002a492611 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -941,8 +941,6 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates= if item["qubits"] == line: cx_errors.append(item["parameters"][0]["value"]) break - else: - continue for qubit in range(num_qubits): for item in props["qubits"][qubit]: if item["name"] == "readout_error": From 5b2d5ecb2ee759dcb7b99d4df83f1e5333bb80ec Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 30 Mar 2022 18:05:34 -0400 Subject: [PATCH 13/14] Update docstring to say qubit_coordinates is explicitly optional --- qiskit/visualization/gate_map.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 56002a492611..66237299852f 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -67,7 +67,7 @@ def plot_gate_map( font_color (str): The font color for the qubit labels. ax (Axes): A Matplotlib axes instance. filename (str): file path to save image to. - qubit_coordinates (Sequence): A sequence type (list or array being the + qubit_coordinates (Sequence): An optional sequence input (list or array being the most common) of 2d coordinates for each qubit. The length of the sequence much match the number of qubits on the backend. The sequence should be the planar coordinates in a 0-based square grid where each @@ -756,7 +756,7 @@ def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None circuit (QuantumCircuit): Input quantum circuit. backend (BaseBackend): Target backend. view (str): Layout view: either 'virtual' or 'physical'. - qubit_coordinates (Sequence): A sequence type (list or array being the + qubit_coordinates (Sequence): An optional sequence input (list or array being the most common) of 2d coordinates for each qubit. The length of the sequence much mast the number of qubits on the backend. The sequence should be the planar coordinates in a 0-based square grid where each @@ -868,7 +868,7 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates= backend (IBMQBackend): Given backend. figsize (tuple): Figure size in inches. show_title (bool): Show the title or not. - qubit_coordinates (Sequence): A sequence type (list or array being the + qubit_coordinates (Sequence): An optional sequence input (list or array being the most common) of 2d coordinates for each qubit. The length of the sequence much mast the number of qubits on the backend. The sequence should be the planar coordinates in a 0-based square grid where each From a6033bd4a8acbd0230c686bce75692b21529ec6a Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 30 Mar 2022 18:08:31 -0400 Subject: [PATCH 14/14] Fix lint --- qiskit/visualization/gate_map.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiskit/visualization/gate_map.py b/qiskit/visualization/gate_map.py index 66237299852f..52c4ff39ab29 100644 --- a/qiskit/visualization/gate_map.py +++ b/qiskit/visualization/gate_map.py @@ -962,7 +962,9 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates= read_err[qargs[0]] = inst_props.error elif len(qargs) == 1: if inst_props.error is not None: - single_gate_errors[qargs[0]] = max(single_gate_errors[qargs[0]], inst_props.error) + single_gate_errors[qargs[0]] = max( + single_gate_errors[qargs[0]], inst_props.error + ) elif len(qargs) == 2: if inst_props.error is not None: two_q_error_map[qargs] = max(