def rshift(self, target): """Add a target backend to circuit. Parameters: target (BaseBackend): The target backend Returns: QuantumCircuit: QuantumCircuit with attached target_backend. Raises: KaleidoscopeError: Input is not a valid backend instance. KaleidoscopeError: Number of qubits larger than target backend. Example: .. jupyter-execute:: from qiskit import QuantumCircuit import kaleidoscope.qiskit from kaleidoscope.qiskit.services import Simulators qc = QuantumCircuit(5, 5) >> Simulators.aer_vigo_simulator print(qc.target_backend) """ if not isinstance(target, BaseBackend): raise KaleidoscopeError('Target is not a valid backend instance.') if self.num_qubits > target.configuration().num_qubits: raise KaleidoscopeError('Number of qubits larger than target backend.') self.target_backend = target # pylint: disable=attribute-defined-outside-init return self
def __ne__(self, other): if (other is not None) and not isinstance(other, (bool, int)): raise KaleidoscopeError('Can only compare against boolean and int values.') if isinstance(other, int) and other not in [0, 1]: raise KaleidoscopeError('Integer comparison must be against 0 or 1.') out = [back for back in self._full_data if back.configuration().open_pulse != other] return BackendCollection(out)
def __call__(self, name=None): """Return all backends that satisfy the given criteria. If no criteria passed then returns all systems. Parameters: name (str or list): System name(s). Returns: IBMQBackend: A single backend instance if only one. BackendCollection: List of specified backends if more than one. Raises: KaleidoscopeError: No matching backends. """ if name is not None: if not isinstance(name, list): out = [ back for back in self._default_added_backends if back.name() == name ] if any(out): return out[0] raise KaleidoscopeError('No matching backend name.') out = [] for nm in name: out += [ back for back in self._default_added_backends if back.name() == nm ] if any(out): return BackendCollection(out) raise KaleidoscopeError('No matching backend names.') return BackendCollection(self._default_added_backends)
def __ge__(self, other): if not isinstance(other, (str, datetime)): raise KaleidoscopeError('Equality can only be checked against a string or datetime') if isinstance(other, datetime): if other <= datetime.now(): raise KaleidoscopeError('Datetime must be in the future.') tvals = (other, None) else: tvals = (time_intervals(other)[0], None) return self.get_reservations(tvals)
def set_default_provider(self, hub=None, group=None, project=None, overwrite=False): """Set the default provider for IBM Q systems. Parameters: hub (str, AccountProvider): A hub name, or Qiskit provider instance group (str): A group name. project (str): A project name. overwrite (bool): Overwrite if already set. Raises: KaleidoscopeError: Input not a valid provider. KaleidoscopeError: Could not load kalrc. KaleidoscopeError: Default provider found and overwrite=False. """ if isinstance(hub, AccountProvider): _temp = hub hub = _temp.credentials.hub group = _temp.credentials.group project = _temp.credentials.project else: pro = self.providers(hub, group, project) if not pro: raise KaleidoscopeError( 'Input hub, group, and/or project not valid.') if len(pro) > 1: raise KaleidoscopeError('Inputs do not specify a unique provider.') pro = pro[0] hub = pro.credentials.hub group = pro.credentials.group project = pro.credentials.project provider_str = "//".join([hub, group, project]) has_rc, rc_file = has_kal_rc() if not has_rc: raise KaleidoscopeError('Could not load kalrc.') if has_rc_key(rc_file, 'default_provider'): if not overwrite: raise KaleidoscopeError( 'Default provider already set, use overwrite=True.') write_rc_key(rc_file, 'default_provider', provider_str) # Trigger a refresh of the Systems provider from kaleidoscope.qiskit.services import Systems # pylint: disable=cyclic-import Systems._refresh()
def __le__(self, other): if not isinstance(other, int): raise KaleidoscopeError('Can only compare against ints') out = [] for back in self: if hasattr(back.configuration(), 'quantum_volume'): qv_val = back.configuration().quantum_volume if qv_val and qv_val <= other: out.append(back) return BackendCollection(out)
def __le__(self, other): if not isinstance(other, (str, datetime)): raise KaleidoscopeError('Equality can only be checked against a string or datetime') if isinstance(other, datetime): tvals = (None, other) else: tvals = (None, time_intervals(other)[1]) return self.get_reservations(tvals)
def get_default_provider(self): """Return the default provider, if any. Returns: str: Default provider. Raises: KaleidoscopeError: Could not load rcfile. """ has_rc, rc_file = has_kal_rc() if not has_rc: raise KaleidoscopeError('Could not load kalrc.') return get_rc_key(rc_file, 'default_provider')
def _bit_string_index(s): """Return the index of a string of 0s and 1s. Parameters: s (str): Bitstring. Returns: int: Index. Raises: KaleidoscopeError: If string is not binary. """ n = len(s) k = s.count("1") if s.count("0") != n - k: raise KaleidoscopeError("s must be a string of 0 and 1") ones = [pos for pos, char in enumerate(s) if char == "1"] return _lex_index(n, k, ones)
def _lex_index(n, k, lst): """Return the lex index of a combination.. Args: n (int): the total number of options . k (int): The number of elements. lst (list): list Returns: int: returns int index for lex order Raises: KaleidoscopeError: if length of list is not equal to k """ if len(lst) != k: raise KaleidoscopeError("list should have length k") comb = list(map(lambda x: n - 1 - x, lst)) dualm = sum([spsp.comb(comb[k - 1 - i], i + 1) for i in range(k)]) return int(dualm)
def get_rc_key(rc_file, key): """Get the desired key from an kal rc_file Parameters: rc_file (str): String specifying file location. key (str): The key name to be written. Returns: str: The key value. Raises: NoSectionError: rc_file missing kaleidoscope section. KaleidoscopeError: Key not found. """ config = configparser.ConfigParser() config.read(rc_file) if not config.has_section('kaleidoscope'): raise configparser.NoSectionError('kaleidoscope') opts = config.options('kaleidoscope') if key not in opts: raise KaleidoscopeError('Key not found.') return config.get('kaleidoscope', key)
def time_intervals(interval): """Computes the start and ending datetimes for a set of commonly used intervals. All times are converted into the local timezone by applying the UTC offset for the locality. Current allowed ``interval`` values are: - 'now' - 'today' - 'tomorrow' - 'this week' - 'next week' - 'this month' - 'next month' Parameters: interval (str): A string representing the human readable interval. Returns: tuple: Start and end datetimes for specified time interval. Raises: KaleidoscopeError: Input string does not correspond to a known time interval. """ now = datetime.now(timezone.utc) week_day = datetime.today().weekday() days_in_month = calendar.monthrange(now.year, now.month)[1] # Get UTC offset in hours dd = now.astimezone() utc_offset = dd.utcoffset() // timedelta(hours=1) today_start = datetime(year=now.year, month=now.month, day=now.day, hour=0, minute=0, second=0) today_end = datetime(year=now.year, month=now.month, day=now.day, hour=23, minute=59, second=59) if interval == 'now': return now, now elif interval == 'today': return now, today_end - timedelta(hours=utc_offset) elif interval == 'tomorrow': start = today_end + timedelta(seconds=1) end = datetime(year=start.year, month=start.month, day=start.day, hour=23, minute=59, second=59) return start - timedelta(hours=utc_offset), end - timedelta( hours=utc_offset) elif interval == 'this week': start = now end = today_end + timedelta(days=6 - week_day) return start, end - timedelta(hours=utc_offset) elif interval == 'next week': start = today_start - timedelta(days=week_day - 7) end = today_end + timedelta(days=13 - week_day) return start - timedelta(hours=utc_offset), end - timedelta( hours=utc_offset) elif interval == 'this month': start = now end = datetime(year=now.year, month=now.month, day=days_in_month, hour=23, minute=59, second=59) return start, end - timedelta(hours=utc_offset) elif interval == 'next month': start = today_start + timedelta(days=days_in_month - now.day + 1) _days_in_month = calendar.monthrange(now.year, start.month)[1] end = datetime(year=start.year, month=start.month, day=_days_in_month, hour=23, minute=59, second=59) return start - timedelta(hours=utc_offset), end - timedelta( hours=utc_offset) else: raise KaleidoscopeError( "Interval must be 'now', 'today', 'tomorrow', 'this week', \ 'next week', 'this month', or 'next month'.")
def qsphere(state, state_labels=True, state_labels_kind='bits', as_widget=False): """Plots a statevector of qubits using the qsphere representation. Parameters: state (ndarray): Statevector as 1D NumPy array. state_labels (bool): Show state labels. state_labels_kind (str): 'bits' (default) or 'ints'. as_widget (bool): Return a widget instance. Returns: PlotlyFigure or PlotlyWidget: Figure instance. Raises: KaleidoscopeError: Invalid statevector input. Example: .. jupyter-execute:: from qiskit import QuantumCircuit from qiskit.quantum_info import Statevector import kaleidoscope.qiskit from kaleidoscope.interactive import qsphere qc = QuantumCircuit(3) qc.h(range(3)) qc.ch(0,1) qc.s(2) qc.cz(2,1) state = qc.statevector() qsphere(state) """ if state.__class__.__name__ in ['Statevector'] \ and 'qiskit' in state.__class__.__module__: state = state.data if state.__class__.__name__ in ['DensityMatrix'] \ and 'qiskit' in state.__class__.__module__: if not abs(1 - state.data.dot(state.data).trace().real) < 1e-6: raise KaleidoscopeError( 'Input density matrix is not a pure state.') # pylint: disable=unexpected-keyword-arg _, evecs = la.eig(state.data) state = evecs[0].ravel() if len(state.shape) == 2: if not abs(1 - state.dot(state).trace().real) < 1e-6: raise KaleidoscopeError( 'Input density matrix is not a pure state.') # pylint: disable=unexpected-keyword-arg _, evecs = la.eig(state.data) state = evecs[0].ravel() if len(state.shape) != 1: raise KaleidoscopeError('Input state is not 1D array.') if np.log2(state.shape[0]) % 1: raise KaleidoscopeError('Input is not a valid statevector of qubits.') eps = 1e-8 norm = mpl.colors.Normalize(vmin=0, vmax=2 * np.pi) cmap = cc.cm.CET_C1s num_qubits = int(np.log2(state.shape[0])) xvals = [] yvals = [] zvals = [] colors = [] bases = [] probs = [] marker_sizes = [] for idx in range(2**num_qubits): prob = (state[idx] * state[idx].conj()).real if prob > eps: elem = bin(idx)[2:].zfill(num_qubits) weight = elem.count("1") zvalue = -2 * weight / num_qubits + 1 number_of_divisions = spsp.comb(num_qubits, weight) weight_order = _bit_string_index(elem) angle = (float(weight) / num_qubits) * (np.pi * 2) + \ (weight_order * 2 * (np.pi / number_of_divisions)) if (weight > num_qubits / 2) or ( ((weight == num_qubits / 2) and (weight_order >= number_of_divisions / 2))): angle = np.pi - angle - (2 * np.pi / number_of_divisions) xvalue = np.sqrt(1 - zvalue**2) * np.cos(angle) yvalue = np.sqrt(1 - zvalue**2) * np.sin(angle) bases.append(elem) probs.append(prob) xvals.append(xvalue) yvals.append(yvalue) zvals.append(zvalue) phase = np.arctan2(state[idx].imag, state[idx].real) phase = phase if phase >= 0 else phase + 2 * np.pi colors.append(mpl.colors.rgb2hex(cmap(norm(phase)))) marker_sizes.append(np.sqrt(prob) * 40) if state_labels_kind == 'ints': bases = [int(kk, 2) for kk in bases] # Output figure instance fig = make_subplots(rows=5, cols=5, specs=[[{ "type": "scene", "rowspan": 5, "colspan": 5 }, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None], [ None, None, None, None, { "rowspan": 1, "colspan": 1, "type": "domain" } ]]) figsize = (350, 350) # List for vector annotations, if any fig_annotations = [] fig.add_trace(BSPHERE(), row=1, col=1) # latitudes for kk in _qsphere_latitudes(zvals): fig.add_trace(kk, row=1, col=1) fig.add_trace(go.Scatter3d( x=[0], y=[0], z=[0], mode='markers', opacity=0.6, marker=dict(size=4, color='#555555'), ), row=1, col=1) for kk, _ in enumerate(xvals): fig.add_trace(go.Scatter3d(x=[0, xvals[kk]], y=[0, yvals[kk]], z=[0, zvals[kk]], mode="lines", hoverinfo=None, opacity=0.5, line=dict(color=colors[kk], width=3)), row=1, col=1) if state_labels: xanc = 'center' if xvals[kk] != 0: if xvals[kk] < 0: xanc = 'right' else: pass yanc = 'middle' if zvals[kk] != 0: if zvals[kk] < 0: yanc = 'top' else: yanc = 'bottom' fig_annotations.append( dict( showarrow=False, x=xvals[kk] * 1.1, y=yvals[kk] * 1.1, z=zvals[kk] * 1.1, text="<b>|{}\u3009</b>".format(bases[kk]), align='left', opacity=0.7, xanchor=xanc, yanchor=yanc, xshift=10, bgcolor="#ffffff", font=dict( size=10, color="#000000", ), )) fig.add_trace(go.Scatter3d( x=xvals, y=yvals, z=zvals, mode='markers', opacity=1, marker=dict(size=marker_sizes, color=colors), ), row=1, col=1) slices = 128 labels = [''] * slices values = [1] * slices phase_colors = [ mpl.colors.rgb2hex(cmap(norm(2 * np.pi * kk / slices))) for kk in range(slices) ] fig.add_trace(go.Pie(labels=labels, values=values, hole=.6, showlegend=False, textinfo='none', hoverinfo='none', textposition="outside", rotation=90, textfont_size=12, marker=dict(colors=phase_colors)), row=5, col=5) pie_x = fig.data[-1]['domain']['x'] pie_y = fig.data[-1]['domain']['y'] fig['layout'].update(annotations=[ dict( xref='paper', yref='paper', x=(pie_x[1] - pie_x[0]) / 2 + pie_x[0], y=(pie_y[1] - pie_y[0]) / 2 + pie_y[0], text='Phase', xanchor="center", yanchor="middle", showarrow=False, font=dict(size=9), ), dict( xref='paper', yref='paper', x=pie_x[0] - 0.03, y=(pie_y[1] - pie_y[0]) / 2 + pie_y[0], text='\U0001D70B', xanchor="left", yanchor="middle", showarrow=False, font=dict(size=14), ), dict( xref='paper', yref='paper', x=pie_x[1] + 0.03, y=(pie_y[1] - pie_y[0]) / 2 + pie_y[0], text='0', xanchor="right", yanchor="middle", showarrow=False, font=dict(size=12), ), dict( xref='paper', yref='paper', x=(pie_x[1] - pie_x[0]) / 2 + pie_x[0], y=pie_y[1] + 0.05, text='\U0001D70B/2', xanchor="center", yanchor="top", showarrow=False, font=dict(size=12), ), dict( xref='paper', yref='paper', x=(pie_x[1] - pie_x[0]) / 2 + pie_x[0], y=pie_y[0] - 0.05, text='3\U0001D70B/2', xanchor="center", yanchor="bottom", showarrow=False, font=dict(size=12), ) ]) fig.update_layout(width=figsize[0], height=figsize[1], autosize=False, hoverdistance=50, showlegend=False, scene_aspectmode='cube', margin=dict(r=15, b=15, l=15, t=15), scene=dict(annotations=fig_annotations, xaxis=dict(showbackground=False, range=[-1.2, 1.2], showspikes=False, visible=False), yaxis=dict(showbackground=False, range=[-1.2, 1.2], showspikes=False, visible=False), zaxis=dict(showbackground=False, range=[-1.2, 1.2], showspikes=False, visible=False)), scene_camera=dict(eye=dict(x=0, y=-1.4, z=0.3))) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig, modebar=True)
def __gt__(self, other): raise KaleidoscopeError('Not implimented')
# -*- coding: utf-8 -*- # This file is part of Kaleidoscope. # # (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. # # Any modifications or derivative works of this code must retain this # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. # pylint: disable=wrong-import-position """Qiskit specific functionality""" from kaleidoscope import HAS_QISKIT from kaleidoscope.errors import KaleidoscopeError if not HAS_QISKIT: raise KaleidoscopeError('Must install qiskit-terra, qiskit-aer, and qiskit-ibmq-provider.') import kaleidoscope.qiskit.overload from .backends.mpl import * from .backends.interactive import system_error_map
def cnot_error_density(backends, figsize=None, colors=None, offset=None, scale='log', covariance_factor=0.1, xlim=None, text_xval=None, xticks=None): """Plot CNOT error distribution for one or more IBMQ backends. Parameters: backends (list or IBMQBackend or BackendProperties): A single or ist of IBMQBackend instances or properties. figsize (tuple): Optional figure size in inches. colors (list): A list of Matplotlib compatible colors to plot with. offset (float): Positive offset for spacing out the backends. scale (str): 'linear' or 'log' scaling of x-axis. covariance_factor (float): Sets the width of the gaussian for each point. xlim (list or tuple): Optional lower and upper limits of cnot error values. text_xval (float): Optional xaxis value at which to start the backend text. xticks (list): Optional list of xaxis ticks to plot. Returns: Figure: A matplotlib Figure instance. Raises: KaleidoscopeError: A backend with < 2 qubits was passed. KaleidoscopeError: Number of colors did not match number of backends. Example: .. jupyter-execute:: from qiskit import * from kaleidoscope.qiskit.backends import cnot_error_density provider = IBMQ.load_account() backends = [] backends.append(provider.backends.ibmq_vigo) backends.append(provider.backends.ibmq_ourense) backends.append(provider.backends.ibmq_london) backends.append(provider.backends.ibmq_burlington) backends.append(provider.backends.ibmq_essex) cnot_error_density(backends) """ if not isinstance(backends, list): backends = [backends] for idx, back in enumerate(backends): if isinstance(back, BackendProperties): backends[idx] = properties_to_pseudobackend(back) for back in backends: if back.configuration().n_qubits < 2: raise KaleidoscopeError('Number of backend qubits must be > 1') if scale not in ['linear', 'log']: raise KaleidoscopeError("scale must be 'linear' or 'log'.") # Attempt to autosize if figsize=None if figsize is None: if len(backends) > 1: fig = plt.figure(figsize=(12, len(backends) * 1.5)) else: fig = plt.figure(figsize=(12, 2)) else: fig = plt.figure(figsize=figsize) text_color = 'k' if offset is None: offset = 100 if len(backends) > 3 else 200 offset = -offset if colors is None: colors = [DARK2[kk % 8] for kk in range(len(backends))] else: if len(colors) != len(backends): raise KaleidoscopeError( 'Number of colors does not match number of backends.') cx_errors = [] for idx, back in enumerate(backends): back_props = back.properties().to_dict() cx_errs = [] meas_errs = [] for gate in back_props['gates']: if len(gate['qubits']) == 2: # Ignore cx gates with values of 1.0 if gate['parameters'][0]['value'] != 1.0: cx_errs.append(gate['parameters'][0]['value']) for qubit in back_props['qubits']: for item in qubit: if item['name'] == 'readout_error': meas_errs.append(item['value']) cx_errors.append(100 * np.asarray(cx_errs)) max_cx_err = max([cerr.max() for cerr in cx_errors]) min_cx_err = min([cerr.min() for cerr in cx_errors]) if xlim is None: if scale == 'linear': xlim = [0, 1.5 * max_cx_err] else: xlim = [ 10**np.floor(np.log10(min_cx_err)), 10**np.ceil(np.log10(max_cx_err)) ] if text_xval is None: if scale == 'linear': text_xval = 0.8 * xlim[1] else: text_xval = 0.6 * xlim[1] for idx, back in enumerate(backends): cx_density = gaussian_kde(cx_errors[idx]) xs = np.linspace(xlim[0], xlim[1], 2500) cx_density.covariance_factor = lambda: covariance_factor cx_density._compute_covariance() if scale == 'linear': plt.plot(xs, 100 * cx_density(xs) + offset * idx, zorder=idx, color=colors[idx]) else: plt.semilogx(xs, 100 * cx_density(xs) + offset * idx, zorder=idx, color=colors[idx]) plt.fill_between(xs, offset * idx, 100 * cx_density(xs) + offset * idx, zorder=idx, color=colors[idx]) qv_val = back.configuration().quantum_volume if qv_val: qv = "(QV" + str(qv_val) + ")" else: qv = '' bname = back.name().split('_')[-1].title() + " {}".format(qv) plt.text(text_xval, offset * idx + 0.2 * (-offset), bname, fontsize=20, color=colors[idx]) fig.axes[0].get_yaxis().set_visible(False) # get rid of the frame for spine in plt.gca().spines.values(): spine.set_visible(False) if xticks is None: if scale == 'linear': xticks = np.round(np.linspace(xlim[0], xlim[1], 4), 2) else: xticks = np.asarray(xticks) if xticks is not None: plt.xticks(np.floor(xticks), labels=np.floor(xticks), color=text_color) plt.xticks(fontsize=18) plt.xlim(xlim) plt.tick_params(axis='x', colors=text_color) plt.xlabel('Gate Error (%)', fontsize=18, color=text_color) plt.title('CNOT Error Distributions', fontsize=18, color=text_color) fig.tight_layout() if mpl.get_backend() in [ 'module://ipykernel.pylab.backend_inline', 'nbAgg' ]: plt.close(fig) return fig
def probability_distribution(data, figsize=(None, None), colors=None, scale='linear', number_to_keep=None, sort='asc', target_string=None, legend=None, bar_labels=True, state_labels_kind='bits', state_labels=None, title=None, plot_background_color='#e5e0df', background_color='#ffffff', as_widget=False): """Interactive histogram plot of probability distributions. Parameters: data (list or dict): This is either a list of dictionaries or a single dict containing the values to represent (ex {'001': 130}) figsize (tuple): Figure size in pixels. colors (list or str): String or list of strings for histogram bar colors. scale (str): Probability scale 'linear' (default) or 'log'. number_to_keep (int): The number of terms to plot and rest is made into a single bar called 'rest'. sort (string): Could be 'asc', 'desc', or 'hamming'. target_string (str): Target string if 'sort' is a distance measure. legend(list): A list of strings to use for labels of the data. The number of entries must match the length of data (if data is a list or 1 if it's a dict) bar_labels (bool): Label each bar in histogram with probability value. state_labels_kind (str): 'bits' (default) or 'ints'. state_labels (list): A list of custom state labels. title (str): A string to use for the plot title. plot_background_color (str): Set the background color behind histogram bars. background_color (str): Set the background color for axes. as_widget (bool): Return figure as an ipywidget. Returns: PlotlyFigure or PlotlyWidget: A figure for the rendered histogram. Raises: ValueError: When legend is provided and the length doesn't match the input data. ImportError: When failed to load plotly. KaleidoscopeError: When state labels is not correct length. Example: .. jupyter-execute:: from qiskit import * import kaleidoscope.qiskit from kaleidoscope.qiskit.services import Simulators from kaleidoscope.interactive import probability_distribution sim = Simulators.aer_vigo_simulator qc = QuantumCircuit(3, 3) >> sim qc.h(1) qc.cx(1,0) qc.cx(1,2) qc.measure(range(3), range(3)) counts = qc.transpile().sample().result_when_done() probability_distribution(counts) """ if sort not in VALID_SORTS: raise ValueError("Value of sort option, %s, isn't a " "valid choice. Must be 'asc', " "'desc', or 'hamming'") if sort in DIST_MEAS.keys() and target_string is None: err_msg = 'Must define target_string when using distance measure.' raise ValueError(err_msg) if isinstance(data, dict): data = [data] if legend and len(legend) != len(data): raise ValueError("Length of legendL (%s) doesn't match " "number of input executions: %s" % (len(legend), len(data))) text_color = find_text_color(background_color) labels = list( sorted(functools.reduce(lambda x, y: x.union(y.keys()), data, set()))) if number_to_keep is not None: labels.append('rest') if sort in DIST_MEAS.keys(): dist = [] for item in labels: dist.append(DIST_MEAS[sort](item, target_string)) labels = [ list(x) for x in zip(*sorted(zip(dist, labels), key=lambda pair: pair[0])) ][1] # Set bar colors if colors is None: if len(data) == 1: colors = COLORS1 elif len(data) == 2: colors = COLORS2 elif len(data) == 3: colors = COLORS3 elif len(data) == 4: colors = COLORS4 elif len(data) == 5: colors = COLORS5 else: colors = COLORS14 elif isinstance(colors, str): colors = [colors] width = 1 / (len(data) + 1) # the width of the bars labels_dict, all_pvalues, _ = _plot_histogram_data(data, labels, number_to_keep) fig = go.Figure() for item, _ in enumerate(data): xvals = [] yvals = [] for idx, val in enumerate(all_pvalues[item]): xvals.append(idx + item * width) yvals.append(val) labels = list(labels_dict.keys()) if state_labels_kind == 'ints': labels = [int(label, 2) for label in labels] if state_labels: if len(state_labels) != len(labels): raise KaleidoscopeError( 'Number of input state labels does not match data.') labels = state_labels hover_template = "<b>{x}</b><br>P = {y}" hover_text = [ hover_template.format(x=labels[kk], y=np.round(yvals[kk], 3)) for kk in range(len(yvals)) ] fig.add_trace( go.Bar(x=xvals, y=yvals, width=width, hoverinfo="text", hovertext=hover_text, marker_color=colors[item % len(colors)], name=legend[item] if legend else '', text=np.round(yvals, 3) if bar_labels else None, textposition='auto')) xaxes_labels = list(labels_dict.keys()) if state_labels_kind == 'ints': xaxes_labels = [int(label, 2) for label in xaxes_labels] if state_labels: if len(state_labels) != len(xaxes_labels): raise KaleidoscopeError( 'Number of input state labels does not match data.') xaxes_labels = state_labels fig.update_xaxes( title='Basis state', titlefont_size=16, tickvals=list(range(len(labels_dict.keys()))), ticktext=xaxes_labels, tickfont_size=14, showline=True, linewidth=1, linecolor=text_color if text_color == 'white' else None, ) fig.update_yaxes( title='Probability', titlefont_size=16, tickfont_size=14, showline=True, linewidth=1, linecolor=text_color if text_color == 'white' else None, ) if scale == 'log': lower = np.min( [min(item.values()) / sum(item.values()) for item in data]) lower = int(np.floor(np.log10(lower))) fig.update_yaxes(type="log", range=[lower, 0]) fig.update_layout(yaxis=dict( tickmode='array', tickvals=[10**k for k in range(lower, 1)], ticktext=["10<sup>{}</sup>".format(k) for k in range(lower, 1)])) fig.update_layout( xaxis_tickangle=-70, showlegend=(legend is not None), width=figsize[0], height=figsize[1], plot_bgcolor=plot_background_color, paper_bgcolor=background_color, title=dict(text=title, x=0.5), title_font_size=18, font=dict(color=text_color), ) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig)
def __le__(self, other): if not isinstance(other, int): raise KaleidoscopeError('Can only compare against ints') out = [back for back in self if back.configuration().max_shots <= other] return BackendCollection(out)
def bloch_sphere(vectors=None, vectors_color=None, vectors_alpha=None, vectors_annotation=False, points=None, points_color=None, points_alpha=None, figsize=(350, 350), label_fontsize=16, annotation_fontsize=10, as_widget=False): """Generates a Bloch sphere from a given collection of vector and/or points data expressed in cartesian coordinates, [x, y, z]. Parameters: vectors (list, ndarray): Collection of one or more vectors to display. vectors_color (str or list): List of colors to use when plotting vectors. vectors_alpha (float or list): List of alphas to use when plotting vectors. vectors_annotation (bool or list): Boolean values to determine if a annotation should be displayed. points (list, ndarray): Collection of one or more points to display. points_color (str or list): List of colors to use when plotting points. points_alpha (float or list): List of alphas to use when plotting points. figsize (tuple): Figure size in pixels. label_fontsize (int): Font size for axes labels. annotation_fontsize (int): Font size for annotations. as_widget (bool): Return plot as a widget. Returns: PlotlyFigure or PlotlyWidget: A Plotly figure or widget instance Raises: ValueError: Input lengths do not match. KaleidoscopeError: Invalid vector input. Example: .. jupyter-execute:: import numpy as np from matplotlib.colors import LinearSegmentedColormap, rgb2hex from kaleidoscope.interactive import bloch_sphere cm = LinearSegmentedColormap.from_list('graypurple', ["#999999", "#AA00FF"]) pointsx = [[0, -np.sin(th), np.cos(th)] for th in np.linspace(0, np.pi/2, 20)] pointsz = [[np.sin(th), -np.cos(th), 0] for th in np.linspace(0, 3*np.pi/4, 30)] points = pointsx + pointsz points_alpha = [np.linspace(0.8, 1, len(points))] points_color = [[rgb2hex(cm(kk)) for kk in np.linspace(-1,1,len(points))]] vectors_color = ["#777777", "#AA00FF"] bloch_sphere(points=points, vectors=[[0, 0, 1], [1/np.sqrt(2), 1/np.sqrt(2), 0]], vectors_color=vectors_color, points_alpha=points_alpha, points_color=points_color) """ # Output figure instance fig = go.Figure() # List for vector annotations, if any fig_annotations = [] idx = 0 if points is not None: nest_depth = nest_level(points) # Take care of single point passed if nest_depth == 1: points = [[points]] # A single list of points passes elif nest_depth == 2: points = [points] # nest_depth = 3 means multiple lists passed if points_color is None: # passed a single point if nest_depth == 1: points_color = [DARK2[0]] elif nest_depth == 2: points_color = [[ DARK2[kk % 8] for kk in range(len(points[0])) ]] elif nest_depth == 3: points_color = [] for kk, pnts in enumerate(points): points_color.append(DARK2[kk % 8] * len(pnts)) if nest_depth == 2 and nest_level(points_color) == 1: points_color = [points_color] if isinstance(points_color, str): points_color = [points_color] if points_alpha is None: points_alpha = [[1.0] * len(p) for p in points] if nest_depth == 2 and nest_level(points_alpha) == 1: points_alpha = [points_alpha] if isinstance(points_alpha, (int, float)): points_alpha = [[points_alpha]] for idx, point_collection in enumerate(points): x_pnts = [] y_pnts = [] z_pnts = [] if isinstance(points_color[idx], str): _colors = [points_color[idx]] * len(point_collection) else: _colors = points_color[idx] if len(points_alpha[idx]) != len(point_collection): err_str = 'number of alpha values ({}) does not equal number of points ({})' raise ValueError( err_str.format(len(points_alpha[idx]), len(x_pnts))) mcolors = [] for kk, point in enumerate(point_collection): x_pnts.append(point[0]) y_pnts.append(point[1]) z_pnts.append(point[2]) mcolors.append("rgba({},{},{},{})".format( *hex_to_rgb(_colors[kk]), points_alpha[idx][kk])) fig.add_trace( go.Scatter3d( x=x_pnts, y=y_pnts, z=z_pnts, mode='markers', marker=dict(size=7, color=mcolors), )) idx += 1 if vectors is not None: if vectors.__class__.__name__ in ['Statevector'] \ and 'qiskit' in vectors.__class__.__module__: vectors = bloch_components(vectors.data) elif not isinstance(vectors[0], (list, np.ndarray)): if vectors[0].__class__.__name__ not in ['Statevector']: vectors = [[vectors[0], vectors[1], vectors[2]]] new_vecs = [] for vec in vectors: if vec.__class__.__name__ in [ 'Statevector' ] and 'qiskit' in vec.__class__.__module__: # pylint: disable=no-member new_vecs.append(bloch_components(vec.data)[0]) else: nst_lvl = nest_level(vec) if nst_lvl == 1: new_vecs.append(vec) elif nst_lvl == 2: new_vecs.append(vec[0]) else: raise KaleidoscopeError("Invalid vector input.") if vectors_color is None: vectors_color = [ DARK2[kk + idx % 8] for kk in range(len(new_vecs)) ] if isinstance(vectors_color, str): vectors_color = [vectors_color] if vectors_alpha is None: vectors_alpha = [1.0] * len(new_vecs) if isinstance(vectors_alpha, (int, float)): vectors_alpha = [vectors_alpha] if vectors_annotation is True: vectors_annotation = [True] * len(new_vecs) elif not vectors_annotation: vectors_annotation = [False] * len(new_vecs) eps = 1e-12 for idx, vec in enumerate(new_vecs): vec = np.asarray(vec) if np.linalg.norm(vec) > 1.0 + eps: raise ValueError('Vector norm must be <= 1.') # So that line does not go out of arrow head vec_line = vec / 1.05 color_str = "rgba({},{},{},{})".format( *hex_to_rgb(vectors_color[idx]), vectors_alpha[idx]) fig.add_trace( go.Scatter3d(x=[0, vec_line[0]], y=[0, vec_line[1]], z=[0, vec_line[2]], mode="lines", hoverinfo=None, line=dict(color=color_str, width=10))) fig.add_trace( go.Cone(x=[vec[0]], y=[vec[1]], z=[vec[2]], u=[vec[0]], v=[vec[1]], w=[vec[2]], sizemode="absolute", showscale=False, opacity=vectors_alpha[idx], colorscale=[vectors_color[idx], vectors_color[idx]], sizeref=0.25, anchor="tip")) if vectors_annotation[idx]: fig_annotations.append( dict( showarrow=False, x=vec[0] * 1.05, y=vec[1] * 1.05, z=vec[2] * 1.05, text="[{},<br> {},<br> {}]".format( round(vec[0], 3), round(vec[1], 3), round(vec[2], 3)), align='left', borderpad=3, xanchor='right' if vec[1] <= 0 else "left", xshift=10, bgcolor="#53565F", font=dict( size=annotation_fontsize, color="#ffffff", family="Courier New, monospace", ), )) # Start construction of sphere # Sphere fig.add_trace(BSPHERE()) # latitudes for kk in LATS: fig.add_trace(kk) # longitudes for kk in LONGS: fig.add_trace(kk) # z-axis fig.add_trace(ZAXIS) # x-axis fig.add_trace(XAXIS) # y-axis fig.add_trace(YAXIS) # zaxis label fig.add_trace(Z0LABEL(fontsize=label_fontsize)) fig.add_trace(Z1LABEL(fontsize=label_fontsize)) # xaxis label fig.add_trace(XLABEL(fontsize=label_fontsize)) # yaxis label fig.add_trace(YLABEL(fontsize=label_fontsize)) fig.update_layout(width=figsize[0], height=figsize[1], autosize=False, hoverdistance=50, showlegend=False, scene_aspectmode='cube', margin=dict(r=0, b=0, l=0, t=0), scene=dict(annotations=fig_annotations, xaxis=dict(showbackground=False, range=[-1.2, 1.2], showspikes=False, visible=False), yaxis=dict(showbackground=False, range=[-1.2, 1.2], showspikes=False, visible=False), zaxis=dict(showbackground=False, range=[-1.2, 1.2], showspikes=False, visible=False)), scene_camera=dict(eye=dict(x=1.5, y=0.4, z=0.4))) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig, modebar=True)
from kaleidoscope.errors import KaleidoscopeError # This is needed because version info is only generated # at setup. This should only fall back when not using # setup.py lint or style to check. try: from .version import version as __version__ except ImportError: __version__ = '0.0.0' from kaleidoscope.interactive import * try: from qiskit import QuantumCircuit from qiskit.providers.aer import Aer from qiskit.providers.ibmq import IBMQ except ImportError: HAS_QISKIT = False else: HAS_QISKIT = True # Look for config file has_rc, rc_file = config.has_kal_rc() if not has_rc: config.generate_kal_rc() has_rc, rc_file = config.has_kal_rc() # Write open provider as default when generating new file. config.write_rc_key(rc_file, 'default_provider', 'ibm-q//open//main') if not has_rc: raise KaleidoscopeError('Could not find or generate kalrc file.')
def system_error_map(backend, figsize=(800, 500), show_title=True, remove_badcal_edges=True, background_color='white', as_widget=False): """Plot the error map of a device. Args: backend (IBMQBackend or FakeBackend or DeviceSimulator or Properties): Plot the error map for a backend. figsize (tuple, optional): Figure size in pixels. show_title (bool, optional): Whether to show figure title. remove_badcal_edges (bool, optional): Whether to remove bad CX gate calibration data. background_color (str, optional): Background color, either 'white' or 'black'. as_widget (bool, optional): ``True`` if the figure is to be returned as a ``PlotlyWidget``. Otherwise the figure is to be returned as a ``PlotlyFigure``. Returns: PlotlyFigure or PlotlyWidget: The error map figure. Raises: KaleidoscopeError: Invalid input type. Example: .. jupyter-execute:: from qiskit import * from kaleidoscope.qiskit.backends import system_error_map pro = IBMQ.load_account() backend = pro.backends.ibmq_vigo system_error_map(backend) """ if not isinstance( backend, (IBMQBackend, DeviceSimulator, FakeBackend, BackendProperties)): raise KaleidoscopeError( 'Input is not a valid backend or properties object.') if isinstance(backend, BackendProperties): backend = properties_to_pseudobackend(backend) meas_text_color = '#000000' if background_color == 'white': color_map = CMAP text_color = '#000000' plotly_cmap = BMY_PLOTLY elif background_color == 'black': color_map = CMAP text_color = '#FFFFFF' plotly_cmap = BMY_PLOTLY else: raise KaleidoscopeError( '"{}" is not a valid background_color selection.'.format( background_color)) if backend.configuration().simulator and not isinstance( backend, DeviceSimulator): raise KaleidoscopeError('Requires a device backend, not a simulator.') config = backend.configuration() n_qubits = config.n_qubits cmap = config.coupling_map if n_qubits in DEVICE_LAYOUTS.keys(): grid_data = DEVICE_LAYOUTS[n_qubits] else: fig = go.Figure() fig.update_layout(showlegend=False, plot_bgcolor=background_color, paper_bgcolor=background_color, width=figsize[0], height=figsize[1], margin=dict(t=60, l=0, r=0, b=0)) out = PlotlyWidget(fig) return out props = backend.properties().to_dict() t1s = [] t2s = [] for qubit_props in props['qubits']: count = 0 for item in qubit_props: if item['name'] == 'T1': t1s.append(item['value']) count += 1 elif item['name'] == 'T2': t2s.append(item['value']) count += 1 if count == 2: break # U2 error rates single_gate_errors = [0] * n_qubits for gate in props['gates']: if gate['gate'] == 'u2': _qubit = gate['qubits'][0] single_gate_errors[_qubit] = gate['parameters'][0]['value'] # Convert to percent single_gate_errors = 100 * np.asarray(single_gate_errors) avg_1q_err = np.mean(single_gate_errors) max_1q_err = max(single_gate_errors) single_norm = mpl.colors.Normalize(vmin=min(single_gate_errors), vmax=max_1q_err) q_colors = [ mpl.colors.rgb2hex(color_map(single_norm(err))) for err in single_gate_errors ] if n_qubits > 1: line_colors = [] if cmap: 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) # remove bad cx edges if remove_badcal_edges: cx_idx = np.where(cx_errors != 100.0)[0] else: cx_idx = np.arange(len(cx_errors)) avg_cx_err = np.mean(cx_errors[cx_idx]) cx_norm = mpl.colors.Normalize(vmin=min(cx_errors[cx_idx]), vmax=max(cx_errors[cx_idx])) for err in cx_errors: if err != 100.0 or not remove_badcal_edges: line_colors.append( mpl.colors.rgb2hex(color_map(cx_norm(err)))) else: line_colors.append("#ff0000") # Measurement errors read_err = [] for qubit in range(n_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) if n_qubits < 10: num_left = n_qubits num_right = 0 else: num_left = math.ceil(n_qubits / 2) num_right = n_qubits - num_left x_max = max([d[1] for d in grid_data]) y_max = max([d[0] for d in grid_data]) max_dim = max(x_max, y_max) qubit_size = 32 font_size = 14 offset = 0 if cmap: if y_max / max_dim < 0.33: qubit_size = 24 font_size = 10 offset = 1 if n_qubits > 5: right_meas_title = "Readout Error (%)" else: right_meas_title = None if cmap: cx_title = "CNOT Error Rate [Avg. {}%]".format(np.round(avg_cx_err, 3)) else: cx_title = None fig = make_subplots( rows=2, cols=11, row_heights=[0.95, 0.05], vertical_spacing=0.15, specs=[[{ "colspan": 2 }, None, { "colspan": 6 }, None, None, None, None, None, { "colspan": 2 }, None, None], [{ "colspan": 4 }, None, None, None, None, None, { "colspan": 4 }, None, None, None, None]], subplot_titles=("Readout Error (%)", None, right_meas_title, "Hadamard Error Rate [Avg. {}%]".format( np.round(avg_1q_err, 3)), cx_title)) # Add lines for couplings if cmap and n_qubits > 1: for ind, edge in enumerate(cmap): is_symmetric = False if edge[::-1] in cmap: is_symmetric = True y_start = grid_data[edge[0]][0] + offset x_start = grid_data[edge[0]][1] y_end = grid_data[edge[1]][0] + offset x_end = grid_data[edge[1]][1] if is_symmetric: if y_start == y_end: x_end = (x_end - x_start) / 2 + x_start x_mid = x_end y_mid = y_start elif x_start == x_end: y_end = (y_end - y_start) / 2 + y_start x_mid = x_start y_mid = y_end else: x_end = (x_end - x_start) / 2 + x_start y_end = (y_end - y_start) / 2 + y_start x_mid = x_end y_mid = y_end else: if y_start == y_end: x_mid = (x_end - x_start) / 2 + x_start y_mid = y_end elif x_start == x_end: x_mid = x_end y_mid = (y_end - y_start) / 2 + y_start else: x_mid = (x_end - x_start) / 2 + x_start y_mid = (y_end - y_start) / 2 + y_start fig.append_trace(go.Scatter( x=[x_start, x_mid, x_end], y=[-y_start, -y_mid, -y_end], mode="lines", line=dict(width=6, color=line_colors[ind]), hoverinfo='text', hovertext='CX<sub>err</sub>{B}_{A} = {err} %'.format( A=edge[0], B=edge[1], err=np.round(cx_errors[ind], 3))), row=1, col=3) # Add the qubits themselves qubit_text = [] qubit_str = "<b>Qubit {}</b><br>H<sub>err</sub> = {} %" qubit_str += "<br>T1 = {} \u03BCs<br>T2 = {} \u03BCs" for kk in range(n_qubits): qubit_text.append( qubit_str.format(kk, np.round(single_gate_errors[kk], 3), np.round(t1s[kk], 2), np.round(t2s[kk], 2))) if n_qubits > 20: qubit_size = 23 font_size = 11 if n_qubits > 50: qubit_size = 20 font_size = 9 qtext_color = [] for ii in range(n_qubits): if single_gate_errors[ii] > 0.8 * max_1q_err: qtext_color.append('black') else: qtext_color.append('white') fig.append_trace(go.Scatter(x=[d[1] for d in grid_data], y=[-d[0] - offset for d in grid_data], mode="markers+text", marker=go.scatter.Marker(size=qubit_size, color=q_colors, opacity=1), text=[str(ii) for ii in range(n_qubits)], textposition="middle center", textfont=dict(size=font_size, color=qtext_color), hoverinfo="text", hovertext=qubit_text), row=1, col=3) fig.update_xaxes(row=1, col=3, visible=False) _range = None if offset: _range = [-3.5, 0.5] fig.update_yaxes(row=1, col=3, visible=False, range=_range) # H error rate colorbar min_1q_err = min(single_gate_errors) max_1q_err = max(single_gate_errors) if n_qubits > 1: fig.append_trace(go.Heatmap(z=[ np.linspace(min_1q_err, max_1q_err, 100), np.linspace(min_1q_err, max_1q_err, 100) ], colorscale=plotly_cmap, showscale=False, hoverinfo='none'), row=2, col=1) fig.update_yaxes(row=2, col=1, visible=False) fig.update_xaxes(row=2, col=1, tickvals=[0, 49, 99], ticktext=[ np.round(min_1q_err, 3), np.round( (max_1q_err - min_1q_err) / 2 + min_1q_err, 3), np.round(max_1q_err, 3) ]) # CX error rate colorbar if cmap and n_qubits > 1: min_cx_err = min(cx_errors) max_cx_err = max(cx_errors) fig.append_trace(go.Heatmap(z=[ np.linspace(min_cx_err, max_cx_err, 100), np.linspace(min_cx_err, max_cx_err, 100) ], colorscale=plotly_cmap, showscale=False, hoverinfo='none'), row=2, col=7) fig.update_yaxes(row=2, col=7, visible=False) min_cx_idx_err = min(cx_errors[cx_idx]) max_cx_idx_err = max(cx_errors[cx_idx]) fig.update_xaxes(row=2, col=7, tickvals=[0, 49, 99], ticktext=[ np.round(min_cx_idx_err, 3), np.round((max_cx_idx_err - min_cx_idx_err) / 2 + min_cx_idx_err, 3), np.round(max_cx_idx_err, 3) ]) hover_text = "<b>Qubit {}</b><br>M<sub>err</sub> = {} %" # Add the left side meas errors for kk in range(num_left - 1, -1, -1): fig.append_trace(go.Bar( x=[read_err[kk]], y=[kk], orientation='h', marker=dict(color='#c7c7c5'), hoverinfo="text", hoverlabel=dict(font=dict(color=meas_text_color)), hovertext=[hover_text.format(kk, np.round(read_err[kk], 3))]), row=1, col=1) fig.append_trace(go.Scatter(x=[avg_read_err, avg_read_err], y=[-0.25, num_left - 1 + 0.25], mode='lines', hoverinfo='none', line=dict(color=text_color, width=2, dash='dot')), row=1, col=1) fig.update_yaxes(row=1, col=1, tickvals=list(range(num_left)), autorange="reversed") fig.update_xaxes( row=1, col=1, range=[0, 1.1 * max_read_err], tickvals=[0, np.round(avg_read_err, 2), np.round(max_read_err, 2)], showline=True, linewidth=1, linecolor=text_color, tickcolor=text_color, ticks="outside", showgrid=False, zeroline=False) # Add the right side meas errors, if any if num_right: for kk in range(n_qubits - 1, num_left - 1, -1): fig.append_trace(go.Bar( x=[-read_err[kk]], y=[kk], orientation='h', marker=dict(color='#c7c7c5'), hoverinfo="text", hoverlabel=dict(font=dict(color=meas_text_color)), hovertext=[hover_text.format(kk, np.round(read_err[kk], 3))]), row=1, col=9) fig.append_trace(go.Scatter(x=[-avg_read_err, -avg_read_err], y=[num_left - 0.25, n_qubits - 1 + 0.25], mode='lines', hoverinfo='none', line=dict(color=text_color, width=2, dash='dot')), row=1, col=9) fig.update_yaxes( row=1, col=9, tickvals=list(range(n_qubits - 1, num_left - 1, -1)), side='right', autorange="reversed", ) fig.update_xaxes( row=1, col=9, range=[-1.1 * max_read_err, 0], tickvals=[ 0, -np.round(avg_read_err, 2), -np.round(max_read_err, 2) ], ticktext=[0, np.round(avg_read_err, 2), np.round(max_read_err, 2)], showline=True, linewidth=1, linecolor=text_color, tickcolor=text_color, ticks="outside", showgrid=False, zeroline=False) # Makes the subplot titles smaller than the 16pt default for ann in fig['layout']['annotations']: ann['font'] = dict(size=13) title_text = "{} Error Map".format(backend.name()) if show_title else '' fig.update_layout(showlegend=False, plot_bgcolor=background_color, paper_bgcolor=background_color, width=figsize[0], height=figsize[1], title=dict(text=title_text, x=0.452), title_font_size=20, font=dict(color=text_color), margin=dict(t=60, l=0, r=40, b=0)) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig)
def system_error_map(backend, figsize=(None, None), colormap=None, background_color='white', show_title=True, remove_badcal_edges=True, as_widget=False): """Plot the error map of a device. Args: backend (IBMQBackend or FakeBackend or DeviceSimulator or Properties): Plot the error map for a backend. figsize (tuple, optional): Figure size in pixels. colormap (Colormap): A matplotlib colormap. background_color (str, optional): Background color, either 'white' or 'black'. show_title (bool, optional): Whether to show figure title. remove_badcal_edges (bool, optional): Whether to remove bad CX gate calibration data. as_widget (bool, optional): ``True`` if the figure is to be returned as a ``PlotlyWidget``. Otherwise the figure is to be returned as a ``PlotlyFigure``. Returns: PlotlyFigure or PlotlyWidget: The error map figure. Raises: KaleidoscopeError: Invalid input type. Example: .. jupyter-execute:: from qiskit import * from kaleidoscope.qiskit.backends import system_error_map pro = IBMQ.load_account() backend = pro.backends.ibmq_vigo system_error_map(backend) """ if not isinstance( backend, (IBMQBackend, DeviceSimulator, FakeBackend, BackendProperties)): raise KaleidoscopeError( 'Input is not a valid backend or properties object.') if isinstance(backend, BackendProperties): backend = properties_to_pseudobackend(backend) CMAP = BMW PLOTLY_CMAP = cmap_to_plotly(CMAP) if colormap is not None: CMAP = colormap PLOTLY_CMAP = cmap_to_plotly(CMAP) meas_text_color = '#000000' if background_color == 'white': color_map = CMAP text_color = '#000000' plotly_cmap = PLOTLY_CMAP elif background_color == 'black': color_map = CMAP text_color = '#FFFFFF' plotly_cmap = PLOTLY_CMAP else: raise KaleidoscopeError( '"{}" is not a valid background_color selection.'.format( background_color)) if backend.configuration().simulator and not isinstance( backend, DeviceSimulator): raise KaleidoscopeError('Requires a device backend, not a simulator.') config = backend.configuration() n_qubits = config.n_qubits cmap = config.coupling_map if str(n_qubits) in LAYOUTS['layouts'].keys(): kind = 'generic' if backend.name() in LAYOUTS['special_names']: if LAYOUTS['special_names'][backend.name()] in LAYOUTS['layouts'][ str(n_qubits)]: kind = LAYOUTS['special_names'][backend.name()] grid_data = LAYOUTS['layouts'][str(n_qubits)][kind] else: fig = go.Figure() fig.update_layout(showlegend=False, plot_bgcolor=background_color, paper_bgcolor=background_color, width=figsize[0], height=figsize[1], margin=dict(t=60, l=0, r=0, b=0)) out = PlotlyWidget(fig) return out props = backend.properties() freqs = [0] * n_qubits t1s = [0] * n_qubits t2s = [0] * n_qubits alphas = [0] * n_qubits for idx, qubit_props in enumerate(props.qubits): for item in qubit_props: if item.name == 'frequency': freqs[idx] = item.value elif item.name == 'T1': t1s[idx] = item.value elif item.name == 'T2': t2s[idx] = item.value elif item.name == 'anharmonicity': alphas[idx] = item.value # U2 error rates single_gate_errors = [0] * n_qubits single_gate_times = [0] * n_qubits for gate in props.gates: if gate.gate in ['u2', 'sx']: _qubit = gate.qubits[0] for gpar in gate.parameters: if gpar.name == 'gate_error': single_gate_errors[_qubit] = gpar.value elif gpar.name == 'gate_length': single_gate_times[_qubit] = gpar.value # Convert to log10 single_gate_errors = np.log10(np.asarray(single_gate_errors)) avg_1q_err = np.mean(single_gate_errors) max_1q_err = _round_log10_exp(np.max(single_gate_errors), rnd='up', decimals=1) min_1q_err = _round_log10_exp(np.min(single_gate_errors), rnd='down', decimals=1) single_norm = mpl.colors.Normalize(vmin=min_1q_err, vmax=max_1q_err) q_colors = [ mpl.colors.rgb2hex(color_map(single_norm(err))) for err in single_gate_errors ] if n_qubits > 1: line_colors = [] if cmap: cx_errors = [] cx_times = [] for line in cmap: for gate in props.gates: if gate.qubits == line: for gpar in gate.parameters: if gpar.name == 'gate_error': cx_errors.append(gpar.value) elif gpar.name == 'gate_length': cx_times.append(gpar.value) # Convert to array cx_errors = np.log10(np.asarray(cx_errors)) # remove bad cx edges if remove_badcal_edges: cx_idx = np.where(cx_errors != 0.0)[0] else: cx_idx = np.arange(len(cx_errors)) avg_cx_err = np.mean(cx_errors[cx_idx]) min_cx_err = _round_log10_exp(np.min(cx_errors[cx_idx]), rnd='down', decimals=1) max_cx_err = _round_log10_exp(np.max(cx_errors[cx_idx]), rnd='up', decimals=1) cx_norm = mpl.colors.Normalize(vmin=min_cx_err, vmax=max_cx_err) for err in cx_errors: if err != 0.0 or not remove_badcal_edges: line_colors.append( mpl.colors.rgb2hex(color_map(cx_norm(err)))) else: line_colors.append("#ff0000") # Measurement errors read_err = [0] * n_qubits p01_err = [0] * n_qubits p10_err = [0] * n_qubits for qubit in range(n_qubits): for item in props.qubits[qubit]: if item.name == 'readout_error': read_err[qubit] = item.value elif item.name == 'prob_meas0_prep1': p01_err[qubit] = item.value elif item.name == 'prob_meas1_prep0': p10_err[qubit] = item.value read_err = np.asarray(read_err) avg_read_err = np.mean(read_err) max_read_err = np.max(read_err) p01_err = np.asarray(p01_err) p10_err = np.asarray(p10_err) if n_qubits < 10: num_left = n_qubits num_right = 0 else: num_left = math.ceil(n_qubits / 2) num_right = n_qubits - num_left x_max = max([d[1] for d in grid_data]) y_max = max([d[0] for d in grid_data]) max_dim = max(x_max, y_max) qubit_size = 32 font_size = 14 offset = 0 if cmap: if y_max / max_dim < 0.33: qubit_size = 24 font_size = 10 offset = 1 if n_qubits > 5: right_meas_title = "Readout error" else: right_meas_title = None if cmap: cx_title = "CNOT error rate [Avg. {}]".format( '{:.2}\u22C510<sup>{}</sup>'.format(*_pow10_coeffs(avg_cx_err))) else: cx_title = None fig = make_subplots( rows=2, cols=11, row_heights=[0.95, 0.05], vertical_spacing=0.15, specs=[[{ "colspan": 2 }, None, { "colspan": 6 }, None, None, None, None, None, { "colspan": 2 }, None, None], [{ "colspan": 4 }, None, None, None, None, None, { "colspan": 4 }, None, None, None, None]], subplot_titles=("Readout error", None, right_meas_title, "SX error rate [Avg. {}]".format( '{:.2}\u22C510<sup>{}</sup>'.format( *_pow10_coeffs(avg_1q_err))), cx_title)) # Add lines for couplings if cmap and n_qubits > 1: for ind, edge in enumerate(cmap): is_symmetric = False if edge[::-1] in cmap: is_symmetric = True y_start = grid_data[edge[0]][0] + offset x_start = grid_data[edge[0]][1] y_end = grid_data[edge[1]][0] + offset x_end = grid_data[edge[1]][1] if is_symmetric: if y_start == y_end: x_end = (x_end - x_start) / 2 + x_start x_mid = x_end y_mid = y_start elif x_start == x_end: y_end = (y_end - y_start) / 2 + y_start x_mid = x_start y_mid = y_end else: x_end = (x_end - x_start) / 2 + x_start y_end = (y_end - y_start) / 2 + y_start x_mid = x_end y_mid = y_end else: if y_start == y_end: x_mid = (x_end - x_start) / 2 + x_start y_mid = y_end elif x_start == x_end: x_mid = x_end y_mid = (y_end - y_start) / 2 + y_start else: x_mid = (x_end - x_start) / 2 + x_start y_mid = (y_end - y_start) / 2 + y_start cx_str = 'cnot<sub>err</sub> = {err}' cx_str += '<br>𝜏<sub>cx</sub> = {tau} ns' fig.append_trace(go.Scatter( x=[x_start, x_mid, x_end], y=[-y_start, -y_mid, -y_end], mode="lines", line=dict(width=6, color=line_colors[ind]), hoverinfo='text', hovertext=cx_str.format( err='{:.3}\u22C510<sup>{}</sup>'.format( *_pow10_coeffs(cx_errors[ind])), tau=np.round(cx_times[ind], 2))), row=1, col=3) # Add the qubits themselves qubit_text = [] qubit_str = "<b>Qubit {idx}</b>" qubit_str += "<br>freq = {freq} GHz" qubit_str += "<br>T<sub>1</sub> = {t1} \u03BCs" qubit_str += "<br>T<sub>2</sub> = {t2} \u03BCs" qubit_str += "<br>α = {anh} GHz" qubit_str += "<br>sx<sub>err</sub> = {err}" qubit_str += "<br>𝜏<sub>sx</sub> = {tau} ns" for kk in range(n_qubits): qubit_text.append( qubit_str.format( idx=kk, freq=np.round(freqs[kk], 5), t1=np.round(t1s[kk], 2), t2=np.round(t2s[kk], 2), anh=np.round(alphas[kk], 3) if alphas[kk] else 'NA', err='{:.3}\u22C510<sup>{}</sup>'.format( *_pow10_coeffs(single_gate_errors[kk])), tau=np.round(single_gate_times[kk], 2))) if n_qubits > 20: qubit_size = 23 font_size = 11 if n_qubits > 50: qubit_size = 20 font_size = 9 qtext_color = [] for ii in range(n_qubits): qtext_color.append(find_text_color(q_colors[ii])) fig.append_trace(go.Scatter(x=[d[1] for d in grid_data], y=[-d[0] - offset for d in grid_data], mode="markers+text", marker=go.scatter.Marker(size=qubit_size, color=q_colors, opacity=1), text=[str(ii) for ii in range(n_qubits)], textposition="middle center", textfont=dict(size=font_size, color=qtext_color), hoverinfo="text", hovertext=qubit_text), row=1, col=3) fig.update_xaxes(row=1, col=3, visible=False) _range = None if offset: _range = [-3.5, 0.5] fig.update_yaxes(row=1, col=3, visible=False, range=_range) # H error rate colorbar if n_qubits > 1: fig.append_trace(go.Heatmap(z=[ np.linspace(min_1q_err, max_1q_err, 100), np.linspace(min_1q_err, max_1q_err, 100) ], colorscale=plotly_cmap, showscale=False, hoverinfo='none'), row=2, col=1) fig.update_yaxes(row=2, col=1, visible=False) mid_1q_err = _round_log10_exp( (max_1q_err - min_1q_err) / 2 + min_1q_err, rnd='up', decimals=1) fig.update_xaxes(row=2, col=1, tickfont=dict(size=13), tickvals=[0, 49, 99], ticktext=[ '{:.2}\u22C510<sup>{}</sup>'.format( *_pow10_coeffs(min_1q_err)), '{:.2}\u22C510<sup>{}</sup>'.format( *_pow10_coeffs(mid_1q_err)), '{:.2}\u22C510<sup>{}</sup>'.format( *_pow10_coeffs(max_1q_err)), ]) # CX error rate colorbar if cmap and n_qubits > 1: fig.append_trace(go.Heatmap(z=[ np.linspace(min_cx_err, max_cx_err, 100), np.linspace(min_cx_err, max_cx_err, 100) ], colorscale=plotly_cmap, showscale=False, hoverinfo='none'), row=2, col=7) fig.update_yaxes(row=2, col=7, visible=False) mid_cx_err = (max_cx_err - min_cx_err) / 2 + min_cx_err fig.update_xaxes( row=2, col=7, tickfont=dict(size=13), tickvals=[0, 49, 99], ticktext=[ '{:.2}\u22C510<sup>{}</sup>'.format( *_pow10_coeffs(min_cx_err)), '{:.2}\u22C510<sup>{}</sup>'.format( *_pow10_coeffs(mid_cx_err)), '{:.2}\u22C510<sup>{}</sup>'.format(*_pow10_coeffs(max_cx_err)) ]) hover_text = "<b>Qubit {idx}</b>" hover_text += "<br>M<sub>err</sub> = {err}" hover_text += "<br>P<sub>0|1</sub> = {p01}" hover_text += "<br>P<sub>1|0</sub> = {p10}" # Add the left side meas errors for kk in range(num_left - 1, -1, -1): fig.append_trace(go.Bar( x=[read_err[kk]], y=[kk], orientation='h', marker=dict(color='#c7c7c5'), hoverinfo="text", hoverlabel=dict(font=dict(color=meas_text_color)), hovertext=[ hover_text.format(idx=kk, err=np.round(read_err[kk], 4), p01=np.round(p01_err[kk], 4), p10=np.round(p10_err[kk], 4)) ]), row=1, col=1) fig.append_trace(go.Scatter(x=[avg_read_err, avg_read_err], y=[-0.25, num_left - 1 + 0.25], mode='lines', hoverinfo='none', line=dict(color=text_color, width=2, dash='dot')), row=1, col=1) fig.update_yaxes(row=1, col=1, tickvals=list(range(num_left)), autorange="reversed") fig.update_xaxes( row=1, col=1, range=[0, 1.1 * max_read_err], tickvals=[0, np.round(avg_read_err, 2), np.round(max_read_err, 2)], showline=True, linewidth=1, linecolor=text_color, tickcolor=text_color, ticks="outside", showgrid=False, zeroline=False) # Add the right side meas errors, if any if num_right: for kk in range(n_qubits - 1, num_left - 1, -1): fig.append_trace(go.Bar( x=[-read_err[kk]], y=[kk], orientation='h', marker=dict(color='#c7c7c5'), hoverinfo="text", hoverlabel=dict(font=dict(color=meas_text_color)), hovertext=[ hover_text.format(idx=kk, err=np.round(read_err[kk], 4), p01=np.round(p01_err[kk], 4), p10=np.round(p10_err[kk], 4)) ]), row=1, col=9) fig.append_trace(go.Scatter(x=[-avg_read_err, -avg_read_err], y=[num_left - 0.25, n_qubits - 1 + 0.25], mode='lines', hoverinfo='none', line=dict(color=text_color, width=2, dash='dot')), row=1, col=9) fig.update_yaxes( row=1, col=9, tickvals=list(range(n_qubits - 1, num_left - 1, -1)), side='right', autorange="reversed", ) fig.update_xaxes( row=1, col=9, range=[-1.1 * max_read_err, 0], tickvals=[ 0, -np.round(avg_read_err, 2), -np.round(max_read_err, 2) ], ticktext=[0, np.round(avg_read_err, 2), np.round(max_read_err, 2)], showline=True, linewidth=1, linecolor=text_color, tickcolor=text_color, ticks="outside", showgrid=False, zeroline=False) # Makes the subplot titles smaller than the 16pt default for ann in fig['layout']['annotations']: ann['font'] = dict(size=13) title_text = "{} error map".format(backend.name()) if show_title else '' fig.update_layout(showlegend=False, plot_bgcolor=background_color, paper_bgcolor=background_color, width=figsize[0], height=figsize[1], title=dict(text=title_text, x=0.452), title_font_size=20, font=dict(color=text_color), margin=dict(t=60, l=0, r=0, b=0), hoverlabel=dict(font_size=14, font_family="courier,monospace", align='left')) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig)
def __call__(self, name=None, hub=None, group=None, project=None): """Return all backends that satisfy the given criteria. If no criteria passed then returns all systems. Parameters: name (str or list): System name. hub (str or list): Specified hub. group (str or list): Specified group. project (str or list): Specified project. Returns: BackendCollection: List of specified backends. Raises: KaleidoscopeError: No matching backends. """ filtered_backends = self._added_backends if name is not None: _temp = [] if not isinstance(name, list): name = [name] for nm in name: _temp += [ back for back in self._added_backends if back.name() == nm and back in filtered_backends ] filtered_backends = _temp if hub is not None: _temp = [] if not isinstance(hub, list): hub = [hub] for hb in hub: _temp += [ back for back in self._added_backends if back.hub == hb and back in filtered_backends ] filtered_backends = _temp if group is not None: _temp = [] if not isinstance(group, list): group = [group] for gp in group: _temp += [ back for back in self._added_backends if back.group == gp and back in filtered_backends ] filtered_backends = _temp if project is not None: _temp = [] if not isinstance(project, list): project = [project] for pt in project: _temp += [ back for back in self._added_backends if back.project == pt and back in filtered_backends ] filtered_backends = _temp if not any(filtered_backends): raise KaleidoscopeError('No matching systems found.') return BackendCollection(filtered_backends)
def system_gate_map(backend, figsize=(None, None), label_qubits=True, qubit_size=None, line_width=None, font_size=None, qubit_colors="#2f4b7c", qubit_labels=None, line_colors="#2f4b7c", font_color="white", background_color='white', as_widget=False): """Plots an interactive gate map of a device. Args: backend (IBMQBackend or FakeBackend or DeviceSimulator or Properties): Plot the error map for a backend. figsize (tuple): Output figure size (wxh) in pixels. label_qubits (bool): Labels for the qubits. qubit_size (float): Size of qubit marker. line_width (float): Width of lines. font_size (float): Font size of qubit labels. qubit_colors (str or list): A list of colors for the qubits. If a single color is given, it's used for all qubits. qubit_labels (list): A list of qubit labels line_colors (str or list): A list of colors for each line from the coupling map. If a single color is given, it's used for all lines. font_color (str): The font color for the qubit labels. background_color (str): The background color, either 'white' or 'black'. as_widget (bool): Return the figure as a widget. Returns: PlotlyFigure or PlotlyWidget: Returned figure instance. Raises: KaleidoscopeError: Invalid input object. Example: .. jupyter-execute:: from qiskit import * from kaleidoscope.qiskit.backends import system_gate_map pro = IBMQ.load_account() backend = pro.backends.ibmq_vigo system_gate_map(backend) """ if not isinstance( backend, (IBMQBackend, DeviceSimulator, FakeBackend, BackendProperties)): raise KaleidoscopeError( 'Input is not a valid backend or properties object.') if isinstance(backend, BackendProperties): backend = properties_to_pseudobackend(backend) config = backend.configuration() n_qubits = config.n_qubits cmap = config.coupling_map # set coloring if isinstance(qubit_colors, str): qubit_colors = [qubit_colors] * n_qubits if isinstance(line_colors, str): line_colors = [line_colors] * len(cmap) if cmap else [] if str(n_qubits) in LAYOUTS['layouts'].keys(): kind = 'generic' if backend.name() in LAYOUTS['special_names']: if LAYOUTS['special_names'][backend.name()] in LAYOUTS['layouts'][ str(n_qubits)]: kind = LAYOUTS['special_names'][backend.name()] grid_data = LAYOUTS['layouts'][str(n_qubits)][kind] else: fig = go.Figure() fig.update_layout(showlegend=False, plot_bgcolor=background_color, paper_bgcolor=background_color, width=figsize[0], height=figsize[1], margin=dict(t=30, l=0, r=0, b=0)) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig) offset = 0 if cmap: if n_qubits in [14, 15, 16]: offset = 1 if qubit_size is None: qubit_size = 24 if font_size is None: font_size = 10 if line_width is None: line_width = 4 if figsize == (None, None): figsize = (400, 200) elif n_qubits == 27: if qubit_size is None: qubit_size = 24 if font_size is None: font_size = 10 if line_width is None: line_width = 4 if figsize == (None, None): figsize = (400, 300) else: if qubit_size is None: qubit_size = 32 if font_size is None: font_size = 14 if line_width is None: line_width = 6 if figsize == (None, None): figsize = (300, 300) else: if figsize == (None, None): figsize = (300, 300) if qubit_size is None: qubit_size = 30 fig = go.Figure() # Add lines for couplings if cmap: for ind, edge in enumerate(cmap): is_symmetric = False if edge[::-1] in cmap: is_symmetric = True y_start = grid_data[edge[0]][0] + offset x_start = grid_data[edge[0]][1] y_end = grid_data[edge[1]][0] + offset x_end = grid_data[edge[1]][1] if is_symmetric: if y_start == y_end: x_end = (x_end - x_start) / 2 + x_start x_mid = x_end y_mid = y_start elif x_start == x_end: y_end = (y_end - y_start) / 2 + y_start x_mid = x_start y_mid = y_end else: x_end = (x_end - x_start) / 2 + x_start y_end = (y_end - y_start) / 2 + y_start x_mid = x_end y_mid = y_end else: if y_start == y_end: x_mid = (x_end - x_start) / 2 + x_start y_mid = y_end elif x_start == x_end: x_mid = x_end y_mid = (y_end - y_start) / 2 + y_start else: x_mid = (x_end - x_start) / 2 + x_start y_mid = (y_end - y_start) / 2 + y_start fig.add_trace( go.Scatter(x=[x_start, x_mid, x_end], y=[-y_start, -y_mid, -y_end], mode="lines", hoverinfo='none', line=dict(width=line_width, color=line_colors[ind]))) # Add the qubits themselves qubit_text = [] qubit_str = "<b>Qubit {}" for num in range(n_qubits): qubit_text.append( qubit_str.format(qubit_labels[num] if qubit_labels else num)) if qubit_labels is None: qubit_labels = [str(ii) for ii in range(n_qubits)] if n_qubits > 50: if qubit_size is None: qubit_size = 20 if font_size is None: font_size = 9 fig.add_trace( go.Scatter(x=[d[1] for d in grid_data], y=[-d[0] - offset for d in grid_data], mode="markers+text", marker=go.scatter.Marker(size=qubit_size, color=qubit_colors, opacity=1), text=qubit_labels if label_qubits else '', textposition="middle center", textfont=dict(size=font_size, color=font_color), hoverinfo="text" if label_qubits else 'none', hovertext=qubit_text)) fig.update_xaxes(visible=False) _range = None if offset: _range = [-3.5, 0.5] fig.update_yaxes(visible=False, range=_range) fig.update_layout(showlegend=False, plot_bgcolor=background_color, paper_bgcolor=background_color, width=figsize[0], height=figsize[1], margin=dict(t=30, l=0, r=0, b=0)) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig)