diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 63b1db744440..5a8958f19500 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -43,6 +43,8 @@ Added
QuantumCircuit class. (#1172)
- New methods in QuantumCircuit for common circuit metrics:
`size()`, `depth()`, `width()`, `count_ops()`, `num_tensor_factors()` (#1285)
+- Added `backend_monitor` and `backend_overview` Jupyter magics,
+ as well as `plot_coupling_map` (#1231)
- Added a `Layout` object (#1313)
- Added a BasicMapper pass (#1270)
- New `plot_bloch_multivector()` to plot Bloch vectors from a tensored state
diff --git a/qiskit/backends/aer/qasm_simulator_cpp b/qiskit/backends/aer/qasm_simulator_cpp
new file mode 100755
index 000000000000..6976a3dea93a
Binary files /dev/null and b/qiskit/backends/aer/qasm_simulator_cpp differ
diff --git a/qiskit/tools/__init__.py b/qiskit/tools/__init__.py
index 70db777102d0..86f93fcbb7c3 100644
--- a/qiskit/tools/__init__.py
+++ b/qiskit/tools/__init__.py
@@ -16,4 +16,4 @@
"""
from .compiler import (compile, execute)
-from .monitor import job_monitor
+from .monitor.job_monitor import job_monitor
diff --git a/qiskit/tools/jupyter/__init__.py b/qiskit/tools/jupyter/__init__.py
index 015a6e8f1130..63919ece0508 100644
--- a/qiskit/tools/jupyter/__init__.py
+++ b/qiskit/tools/jupyter/__init__.py
@@ -9,10 +9,18 @@
"""
from IPython import get_ipython # pylint: disable=import-error
+from qiskit.tools.visualization._matplotlib import HAS_MATPLOTLIB
from .jupyter_magics import (ProgressBarMagic, StatusMagic)
-from .progressbar import HTMLProgressBar, TextProgressBar
+from .progressbar import HTMLProgressBar
+
+if HAS_MATPLOTLIB:
+ from .backend_overview import BackendOverview
+ from .backend_monitor import BackendMonitor
_IP = get_ipython()
if _IP is not None:
_IP.register_magics(ProgressBarMagic)
_IP.register_magics(StatusMagic)
+ if HAS_MATPLOTLIB:
+ _IP.register_magics(BackendOverview)
+ _IP.register_magics(BackendMonitor)
diff --git a/qiskit/tools/jupyter/backend_monitor.py b/qiskit/tools/jupyter/backend_monitor.py
new file mode 100644
index 000000000000..188bcb7d01dd
--- /dev/null
+++ b/qiskit/tools/jupyter/backend_monitor.py
@@ -0,0 +1,569 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2018, IBM.
+#
+# This source code is licensed under the Apache License, Version 2.0 found in
+# the LICENSE.txt file in the root directory of this source tree.
+
+"""A module for monitoring backends."""
+
+import math
+import datetime
+from IPython.display import display # pylint: disable=import-error
+from IPython.core.magic import (line_magic, # pylint: disable=import-error
+ Magics, magics_class)
+import ipywidgets as widgets # pylint: disable=import-error
+import matplotlib.pyplot as plt
+import matplotlib.colors
+import matplotlib as mpl
+from matplotlib import cm
+from matplotlib.patches import Circle
+from qiskit.backends.ibmq import IBMQ
+from qiskit.qiskiterror import QISKitError
+from qiskit.backends.ibmq.ibmqbackend import IBMQBackend
+from qiskit.tools.visualization._gate_map import plot_gate_map
+
+
+@magics_class
+class BackendMonitor(Magics):
+ """A class of status magic functions.
+ """
+ @line_magic
+ def qiskit_backend_monitor(self, line='', cell=None): # pylint: disable=W0613
+ """A Jupyter magic function to monitor backends.
+ """
+ backend = self.shell.user_ns[line]
+ if not isinstance(backend, IBMQBackend):
+ raise QISKitError('Input variable is not of type IBMQBackend.')
+ title_style = "style='color:#ffffff;background-color:#000000;padding-top: 1%;"
+ title_style += "padding-bottom: 1%;padding-left: 1%; margin-top: 0px'"
+ title_html = "
{name}
".format(
+ style=title_style, name=backend.name())
+
+ details = [config_tab(backend)]
+
+ tab_contents = ['Configuration']
+
+ if not backend.configuration().simulator:
+ tab_contents.extend(['Qubit Properties', 'Multi-Qubit Gates',
+ 'Error Map', 'Job History'])
+ details.extend([qubits_tab(backend), gates_tab(backend),
+ detailed_map(backend), job_history(backend)])
+
+ tabs = widgets.Tab(layout=widgets.Layout(overflow_y='scroll'))
+ tabs.children = details
+ for i in range(len(details)):
+ tabs.set_title(i, tab_contents[i])
+
+ title_widget = widgets.HTML(value=title_html,
+ layout=widgets.Layout(margin='0px 0px 0px 0px'))
+
+ backend_monitor = widgets.VBox([title_widget, tabs],
+ layout=widgets.Layout(border='4px solid #000000',
+ max_height='650px', min_height='650px',
+ overflow_y='hidden'))
+
+ display(backend_monitor)
+
+
+def config_tab(backend):
+ """The backend configuration widget.
+
+ Args:
+ backend (IBMQbackend): The backend.
+
+ Returns:
+ grid: A GridBox widget.
+ """
+ status = backend.status().to_dict()
+ config = backend.configuration().to_dict()
+
+ config_dict = {**status, **config}
+
+ upper_list = ['n_qubits', 'operational',
+ 'status_msg', 'pending_jobs',
+ 'basis_gates', 'local', 'simulator']
+
+ lower_list = list(set(config_dict.keys()).difference(upper_list))
+ # Remove gates because they are in a different tab
+ lower_list.remove('gates')
+ upper_str = ""
+ upper_str += """"""
+
+ footer = "
"
+
+ # Upper HBox widget data
+
+ upper_str += "Property | Value |
"
+ for key in upper_list:
+ upper_str += "%s | %s |
" % (
+ key, config_dict[key])
+ upper_str += footer
+
+ upper_table = widgets.HTML(
+ value=upper_str, layout=widgets.Layout(width='100%', grid_area='left'))
+
+ image_widget = widgets.Output(
+ layout=widgets.Layout(display='flex-inline', grid_area='right',
+ padding='10px 10px 10px 10px',
+ width='auto', max_height='300px',
+ align_items='center'))
+
+ if not config['simulator']:
+ with image_widget:
+ gate_map = plot_gate_map(backend)
+ display(gate_map)
+ plt.close(gate_map)
+
+ lower_str = ""
+ lower_str += """"""
+ lower_str += " | |
"
+ for key in lower_list:
+ if key != 'name':
+ lower_str += "%s | %s |
" % (
+ key, config_dict[key])
+ lower_str += footer
+
+ lower_table = widgets.HTML(value=lower_str,
+ layout=widgets.Layout(
+ width='auto',
+ grid_area='bottom'))
+
+ grid = widgets.GridBox(children=[upper_table, image_widget, lower_table],
+ layout=widgets.Layout(
+ grid_template_rows='auto auto',
+ grid_template_columns='25% 25% 25% 25%',
+ grid_template_areas='''
+ "left right right right"
+ "bottom bottom bottom bottom"
+ ''',
+ grid_gap='0px 0px'))
+
+ return grid
+
+
+def qubits_tab(backend):
+ """The qubits properties widget
+
+ Args:
+ backend (IBMQbackend): The backend.
+
+ Returns:
+ VBox: A VBox widget.
+ """
+ props = backend.properties().to_dict()
+
+ header_html = "{key}: {value}
"
+ header_html = header_html.format(key='last_update_date',
+ value=props['last_update_date'])
+ update_date_widget = widgets.HTML(value=header_html)
+
+ qubit_html = ""
+ qubit_html += """"""
+
+ qubit_html += " | Frequency | T1 | T2 | "
+ qubit_html += "U1 gate error | U2 gate error | U3 gate error | "
+ qubit_html += "Readout error |
"
+ qubit_footer = "
"
+
+ for qub in range(len(props['qubits'])):
+ name = 'Q%s' % qub
+ qubit_data = props['qubits'][qub]
+ gate_data = props['gates'][3*qub:3*qub+3]
+ t1_info = qubit_data[0]
+ t2_info = qubit_data[1]
+ freq_info = qubit_data[2]
+ readout_info = qubit_data[3]
+
+ freq = str(round(freq_info['value'], 5))+' '+freq_info['unit']
+ T1 = str(round(t1_info['value'], # pylint: disable=invalid-name
+ 5))+' ' + t1_info['unit']
+ T2 = str(round(t2_info['value'], # pylint: disable=invalid-name
+ 5))+' ' + t2_info['unit']
+ # pylint: disable=invalid-name
+ U1 = str(round(gate_data[0]['parameters'][0]['value'], 5))
+ # pylint: disable=invalid-name
+ U2 = str(round(gate_data[1]['parameters'][0]['value'], 5))
+ # pylint: disable=invalid-name
+ U3 = str(round(gate_data[2]['parameters'][0]['value'], 5))
+
+ readout_error = round(readout_info['value'], 5)
+ qubit_html += "%s | %s | "
+ qubit_html += "%s | %s | %s | %s | %s | %s |
"
+ qubit_html = qubit_html % (name, freq, T1, T2, U1, U2, U3, readout_error)
+ qubit_html += qubit_footer
+
+ qubit_widget = widgets.HTML(value=qubit_html)
+
+ out = widgets.VBox([update_date_widget,
+ qubit_widget])
+
+ return out
+
+
+def gates_tab(backend):
+ """The multiple qubit gate error widget.
+
+ Args:
+ backend (IBMQbackend): The backend.
+
+ Returns:
+ VBox: A VBox widget.
+ """
+ config = backend.configuration().to_dict()
+ props = backend.properties().to_dict()
+
+ multi_qubit_gates = props['gates'][3*config['n_qubits']:]
+
+ header_html = "{key}: {value}
"
+ header_html = header_html.format(key='last_update_date',
+ value=props['last_update_date'])
+
+ update_date_widget = widgets.HTML(value=header_html,
+ layout=widgets.Layout(grid_area='top'))
+
+ gate_html = ""
+ gate_html += """"""
+
+ gate_html += " | Type | Gate error |
"
+ gate_footer = "
"
+
+ # Split gates into two columns
+ left_num = math.ceil(len(multi_qubit_gates)/3)
+ mid_num = math.ceil((len(multi_qubit_gates)-left_num)/2)
+
+ left_table = gate_html
+
+ for qub in range(left_num):
+ gate = multi_qubit_gates[qub]
+ name = gate['name']
+ ttype = gate['gate']
+ error = round(gate['parameters'][0]['value'], 5)
+
+ left_table += "%s"
+ left_table += " | %s | %s |
"
+ left_table = left_table % (name, ttype, error)
+ left_table += gate_footer
+
+ middle_table = gate_html
+
+ for qub in range(left_num, left_num+mid_num):
+ gate = multi_qubit_gates[qub]
+ name = gate['name']
+ ttype = gate['gate']
+ error = round(gate['parameters'][0]['value'], 5)
+
+ middle_table += "%s"
+ middle_table += " | %s | %s |
"
+ middle_table = middle_table % (name, ttype, error)
+ middle_table += gate_footer
+
+ right_table = gate_html
+
+ for qub in range(left_num+mid_num, len(multi_qubit_gates)):
+ gate = multi_qubit_gates[qub]
+ name = gate['name']
+ ttype = gate['gate']
+ error = round(gate['parameters'][0]['value'], 5)
+
+ right_table += "%s"
+ right_table += " | %s | %s |
"
+ right_table = right_table % (name, ttype, error)
+ right_table += gate_footer
+
+ left_table_widget = widgets.HTML(value=left_table,
+ layout=widgets.Layout(grid_area='left'))
+ middle_table_widget = widgets.HTML(value=middle_table,
+ layout=widgets.Layout(grid_area='middle'))
+ right_table_widget = widgets.HTML(value=right_table,
+ layout=widgets.Layout(grid_area='right'))
+
+ grid = widgets.GridBox(children=[update_date_widget,
+ left_table_widget,
+ middle_table_widget,
+ right_table_widget],
+ layout=widgets.Layout(
+ grid_template_rows='auto auto',
+ grid_template_columns='33% 33% 33%',
+ grid_template_areas='''
+ "top top top"
+ "left middle right"
+ ''',
+ grid_gap='0px 0px'))
+
+ return grid
+
+
+def detailed_map(backend):
+ """Widget for displaying detailed noise map.
+
+ Args:
+ backend (IBMQbackend): The backend.
+
+ Returns:
+ GridBox: Widget holding noise map images.
+ """
+ props = backend.properties().to_dict()
+ config = backend.configuration().to_dict()
+ single_gate_errors = [q['parameters'][0]['value']
+ for q in props['gates'][2:3*config['n_qubits']:3]]
+ single_norm = matplotlib.colors.Normalize(
+ vmin=min(single_gate_errors), vmax=max(single_gate_errors))
+ q_colors = [cm.viridis(single_norm(err)) for err in single_gate_errors]
+
+ cmap = config['coupling_map']
+
+ cx_errors = []
+ for line in cmap:
+ for item in props['gates'][3*config['n_qubits']:]:
+ if item['qubits'] == line:
+ cx_errors.append(item['parameters'][0]['value'])
+ break
+ else:
+ continue
+
+ cx_norm = matplotlib.colors.Normalize(
+ vmin=min(cx_errors), vmax=max(cx_errors))
+ line_colors = [cm.viridis(cx_norm(err)) for err in cx_errors]
+
+ single_widget = widgets.Output(layout=widgets.Layout(display='flex-inline', grid_area='left',
+ align_items='center'))
+
+ cmap_widget = widgets.Output(layout=widgets.Layout(display='flex-inline', grid_area='top',
+ width='auto', height='auto',
+ align_items='center'))
+
+ cx_widget = widgets.Output(layout=widgets.Layout(display='flex-inline', grid_area='right',
+ align_items='center'))
+
+ tick_locator = mpl.ticker.MaxNLocator(nbins=5)
+ with cmap_widget:
+ noise_map = plot_gate_map(backend, qubit_color=q_colors,
+ line_color=line_colors,
+ qubit_size=28,
+ plot_directed=True)
+ width, height = noise_map.get_size_inches()
+
+ noise_map.set_size_inches(1.25*width, 1.25*height)
+
+ display(noise_map)
+ plt.close(noise_map)
+
+ with single_widget:
+ cbl_fig = plt.figure(figsize=(3, 1))
+ ax1 = cbl_fig.add_axes([0.05, 0.80, 0.9, 0.15])
+ single_cb = mpl.colorbar.ColorbarBase(ax1, cmap=cm.viridis,
+ norm=single_norm,
+ orientation='horizontal')
+ single_cb.locator = tick_locator
+ single_cb.update_ticks()
+ ax1.set_title('Single-qubit U3 error rate')
+ display(cbl_fig)
+ plt.close(cbl_fig)
+
+ with cx_widget:
+ cx_fig = plt.figure(figsize=(3, 1))
+ ax2 = cx_fig.add_axes([0.05, 0.80, 0.9, 0.15])
+ cx_cb = mpl.colorbar.ColorbarBase(ax2, cmap=cm.viridis,
+ norm=cx_norm,
+ orientation='horizontal')
+ cx_cb.locator = tick_locator
+ cx_cb.update_ticks()
+ ax2.set_title('CNOT error rate')
+ display(cx_fig)
+ plt.close(cx_fig)
+
+ out_box = widgets.GridBox([single_widget, cmap_widget, cx_widget],
+ layout=widgets.Layout(
+ grid_template_rows='auto auto',
+ grid_template_columns='33% 33% 33%',
+ grid_template_areas='''
+ "top top top"
+ "left . right"
+ ''',
+ grid_gap='0px 0px'))
+ return out_box
+
+
+def job_history(backend):
+ """Widget for displaying job history
+
+ Args:
+ backend (IBMQbackend): The backend.
+
+ Returns:
+ Tab: A tab widget for history images.
+ """
+ year = widgets.Output(layout=widgets.Layout(display='flex-inline',
+ align_items='center',
+ min_height='400px'))
+
+ month = widgets.Output(layout=widgets.Layout(display='flex-inline',
+ align_items='center',
+ min_height='400px'))
+
+ week = widgets.Output(layout=widgets.Layout(display='flex-inline',
+ align_items='center',
+ min_height='400px'))
+
+ tabs = widgets.Tab(layout=widgets.Layout(max_height='620px'))
+ tabs.children = [year, month, week]
+ tabs.set_title(0, 'Year')
+ tabs.set_title(1, 'Month')
+ tabs.set_title(2, 'Week')
+ tabs.selected_index = 1
+
+ _build_job_history(tabs, backend)
+ return tabs
+
+
+def _build_job_history(tabs, backend):
+
+ backends = IBMQ.backends(backend.name())
+ past_year_date = datetime.datetime.now() - datetime.timedelta(days=365)
+ date_filter = {'creationDate': {'gt': past_year_date.isoformat()}}
+ jobs = []
+ for back in backends:
+ jobs.extend(back.jobs(limit=None, db_filter=date_filter))
+
+ with tabs.children[1]:
+ month_plot = plot_job_history(jobs, interval='month')
+ display(month_plot)
+ plt.close(month_plot)
+
+ with tabs.children[0]:
+ year_plot = plot_job_history(jobs, interval='year')
+ display(year_plot)
+ plt.close(year_plot)
+
+ with tabs.children[2]:
+ week_plot = plot_job_history(jobs, interval='week')
+ display(week_plot)
+ plt.close(week_plot)
+
+
+def plot_job_history(jobs, interval='year'):
+ """Plots the job history of the user from the given list of jobs.
+
+ Args:
+ jobs (list): A list of jobs with type IBMQjob.
+ interval (str): Interval over which to examine.
+
+ Returns:
+ fig: A Matplotlib figure instance.
+ """
+ def get_date(job):
+ """Returns a datetime object from a IBMQJob instance.
+
+ Args:
+ job (IBMQJob): A job.
+
+ Returns:
+ dt: A datetime object.
+ """
+ return datetime.datetime.strptime(job.creation_date(),
+ '%Y-%m-%dT%H:%M:%S.%fZ')
+
+ current_time = datetime.datetime.now()
+
+ if interval == 'year':
+ bins = [(current_time - datetime.timedelta(days=k*365/12))
+ for k in range(12)]
+ elif interval == 'month':
+ bins = [(current_time - datetime.timedelta(days=k)) for k in range(30)]
+ elif interval == 'week':
+ bins = [(current_time - datetime.timedelta(days=k)) for k in range(7)]
+
+ binned_jobs = [0]*len(bins)
+
+ if interval == 'year':
+ for job in jobs:
+ for ind, dat in enumerate(bins):
+ date = get_date(job)
+ if date.month == dat.month:
+ binned_jobs[ind] += 1
+ break
+ else:
+ continue
+ else:
+ for job in jobs:
+ for ind, dat in enumerate(bins):
+ date = get_date(job)
+ if date.day == dat.day and date.month == dat.month:
+ binned_jobs[ind] += 1
+ break
+ else:
+ continue
+
+ nz_bins = []
+ nz_idx = []
+ for ind, val in enumerate(binned_jobs):
+ if val != 0:
+ nz_idx.append(ind)
+ nz_bins.append(val)
+
+ total_jobs = sum(binned_jobs)
+
+ colors = ['#003f5c', '#ffa600', '#374c80', '#ff764a',
+ '#7a5195', '#ef5675', '#bc5090']
+
+ if interval == 'year':
+ labels = ['{}-{}'.format(str(bins[b].year)[2:], bins[b].month) for b in nz_idx]
+ else:
+ labels = ['{}-{}'.format(bins[b].month, bins[b].day) for b in nz_idx]
+ fig, ax = plt.subplots(1, 1, figsize=(5, 5)) # pylint: disable=invalid-name
+ ax.pie(nz_bins[::-1], labels=labels, colors=colors, textprops={'fontsize': 14},
+ rotatelabels=True, counterclock=False)
+ ax.add_artist(Circle((0, 0), 0.7, color='white', zorder=1))
+ ax.text(0, 0, total_jobs, horizontalalignment='center',
+ verticalalignment='center', fontsize=26)
+ fig.tight_layout()
+ return fig
diff --git a/qiskit/tools/jupyter/backend_overview.py b/qiskit/tools/jupyter/backend_overview.py
new file mode 100644
index 000000000000..b2f7f098dce8
--- /dev/null
+++ b/qiskit/tools/jupyter/backend_overview.py
@@ -0,0 +1,257 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2018, IBM.
+#
+# This source code is licensed under the Apache License, Version 2.0 found in
+# the LICENSE.txt file in the root directory of this source tree.
+
+"""A module for monitoring backends."""
+
+import time
+import threading
+import types
+from IPython.display import display # pylint: disable=import-error
+from IPython.core.magic import line_magic, Magics, magics_class # pylint: disable=import-error
+from IPython.core import magic_arguments # pylint: disable=import-error
+import ipywidgets as widgets # pylint: disable=import-error
+import matplotlib.pyplot as plt
+from qiskit.tools.monitor.backend_overview import get_unique_backends
+from qiskit.tools.visualization._gate_map import plot_gate_map
+
+
+@magics_class
+class BackendOverview(Magics):
+ """A class of status magic functions.
+ """
+ @line_magic
+ @magic_arguments.magic_arguments()
+ @magic_arguments.argument(
+ '-i',
+ '--interval',
+ type=float,
+ default=60,
+ help='Interval for status check.'
+ )
+ def qiskit_backend_overview(self, line='', cell=None): # pylint: disable=W0613
+ """A Jupyter magic function to monitor backends.
+ """
+ args = magic_arguments.parse_argstring(
+ self.qiskit_backend_overview, line)
+
+ unique_hardware_backends = get_unique_backends()
+ _value = "Backend Overview
"
+ backend_title = widgets.HTML(value=_value,
+ layout=widgets.Layout(margin='0px 0px 0px 0px'))
+
+ build_back_widgets = [backend_widget(b)
+ for b in unique_hardware_backends]
+
+ _backends = []
+ # Sort backends by operational or not
+ oper_ord_backends = []
+ for n, back in enumerate(unique_hardware_backends):
+ if back.status().operational:
+ oper_ord_backends = [build_back_widgets[n]] + oper_ord_backends
+ _backends = [back] + _backends
+ else:
+ oper_ord_backends = oper_ord_backends + [build_back_widgets[n]]
+ _backends = _backends + [back]
+
+ qubit_label = widgets.Label(value='Num. Qubits')
+ pend_label = widgets.Label(value='Pending Jobs')
+ least_label = widgets.Label(value='Least Busy')
+ oper_label = widgets.Label(
+ value='Operational', layout=widgets.Layout(margin='5px 0px 0px 0px'))
+ t1_label = widgets.Label(
+ value='Avg. T1', layout=widgets.Layout(margin='10px 0px 0px 0px'))
+ t2_label = widgets.Label(
+ value='Avg. T2', layout=widgets.Layout(margin='10px 0px 0px 0px'))
+
+ labels_widget = widgets.VBox([qubit_label, pend_label, least_label,
+ oper_label, t1_label, t2_label],
+ layout=widgets.Layout(margin='295px 0px 0px 0px',
+ min_width='100px'))
+
+ backend_grid = GridBox_with_thread(children=oper_ord_backends,
+ layout=widgets.Layout(
+ grid_template_columns='250px ' *
+ len(unique_hardware_backends),
+ grid_template_rows='auto',
+ grid_gap='0px 25px'))
+
+ backend_grid._backends = _backends # pylint: disable=W0201
+ backend_grid._update = types.MethodType( # pylint: disable=W0201
+ update_backend_info, backend_grid)
+
+ backend_grid._thread = threading.Thread( # pylint: disable=W0201
+ target=backend_grid._update, args=(args.interval,))
+ backend_grid._thread.start()
+
+ back_box = widgets.HBox([labels_widget, backend_grid])
+
+ back_monitor = widgets.VBox([backend_title, back_box])
+ display(back_monitor)
+
+
+class GridBox_with_thread(widgets.GridBox): # pylint: disable=invalid-name
+ """A GridBox that will close an attached thread
+ """
+ def __del__(self):
+ """Object disposal"""
+ if hasattr(self, '_thread'):
+ try:
+ self._thread.do_run = False
+ self._thread.join()
+ except Exception: # pylint: disable=W0703
+ pass
+ self.close()
+
+
+def backend_widget(backend):
+ """Creates a backend widget.
+ """
+ config = backend.configuration().to_dict()
+ props = backend.properties().to_dict()
+
+ name = widgets.HTML(value="{name}
".format(name=backend.name()),
+ layout=widgets.Layout())
+
+ n_qubits = config['n_qubits']
+
+ qubit_count = widgets.HTML(value="{qubits}
".format(qubits=n_qubits),
+ layout=widgets.Layout(justify_content='center'))
+
+ cmap = widgets.Output(layout=widgets.Layout(min_width='250px', max_width='250px',
+ max_height='250px',
+ min_height='250px',
+ justify_content='center',
+ align_items='center',
+ margin='0px 0px 0px 0px'))
+
+ with cmap:
+ _cmap_fig = plot_gate_map(backend,
+ plot_directed=False,
+ label_qubits=False)
+ if _cmap_fig is not None:
+ display(_cmap_fig)
+ # Prevents plot from showing up twice.
+ plt.close(_cmap_fig)
+
+ pending = generate_jobs_pending_widget()
+
+ is_oper = widgets.HTML(value="",
+ layout=widgets.Layout(justify_content='center'))
+
+ least_busy = widgets.HTML(value="",
+ layout=widgets.Layout(justify_content='center'))
+
+ t1_units = props['qubits'][0][0]['unit']
+ avg_t1 = round(sum([q[0]['value'] for q in props['qubits']])/n_qubits, 1)
+ t1_widget = widgets.HTML(value="{t1} {units}
".format(t1=avg_t1, units=t1_units),
+ layout=widgets.Layout())
+
+ t2_units = props['qubits'][0][1]['unit']
+ avg_t2 = round(sum([q[1]['value'] for q in props['qubits']])/n_qubits, 1)
+ t2_widget = widgets.HTML(value="{t2} {units}
".format(t2=avg_t2, units=t2_units),
+ layout=widgets.Layout())
+
+ out = widgets.VBox([name, cmap, qubit_count, pending,
+ least_busy, is_oper, t1_widget, t2_widget],
+ layout=widgets.Layout(display='inline-flex',
+ flex_flow='column',
+ align_items='center'))
+
+ out._is_alive = True
+ return out
+
+
+def update_backend_info(self, interval=60):
+ """Updates the monitor info
+ Called from another thread.
+ """
+ my_thread = threading.currentThread()
+ current_interval = 0
+ started = False
+ all_dead = False
+ stati = [None]*len(self._backends)
+ while getattr(my_thread, "do_run", True) and not all_dead:
+ if current_interval == interval or started is False:
+ for ind, back in enumerate(self._backends):
+ _value = self.children[ind].children[2].value
+ _head = _value.split('')[0]
+ try:
+ _status = back.status()
+ stati[ind] = _status
+ except Exception: # pylint: disable=W0703
+ self.children[ind].children[2].value = _value.replace(
+ _head, "")
+ self.children[ind]._is_alive = False
+ else:
+ self.children[ind]._is_alive = True
+ self.children[ind].children[2].value = _value.replace(
+ _head, "")
+
+ idx = list(range(len(self._backends)))
+ pending = [s.pending_jobs for s in stati]
+ _, least_idx = zip(*sorted(zip(pending, idx)))
+
+ # Make sure least pending is operational
+ for ind in least_idx:
+ if stati[ind].operational:
+ least_pending_idx = ind
+ break
+
+ for var in idx:
+ if var == least_pending_idx:
+ self.children[var].children[4].value = "True
"
+ else:
+ self.children[var].children[4].value = "False
"
+
+ self.children[var].children[3].children[1].value = pending[var]
+ self.children[var].children[3].children[1].max = max(
+ self.children[var].children[3].children[1].max, pending[var]+10)
+ if stati[var].operational:
+ self.children[var].children[5].value = "True
"
+ else:
+ self.children[var].children[5].value = "False
"
+
+ started = True
+ current_interval = 0
+ time.sleep(1)
+ all_dead = not any([wid._is_alive for wid in self.children])
+ current_interval += 1
+
+
+def generate_jobs_pending_widget():
+ """Generates a jobs_pending progress bar widget.
+ """
+ pbar = widgets.IntProgress(
+ value=0,
+ min=0,
+ max=50,
+ description='',
+ orientation='horizontal', layout=widgets.Layout(max_width='180px'))
+ pbar.style.bar_color = '#71cddd'
+
+ pbar_current = widgets.Label(
+ value=str(pbar.value), layout=widgets.Layout(min_width='auto'))
+ pbar_max = widgets.Label(
+ value=str(pbar.max), layout=widgets.Layout(min_width='auto'))
+
+ def _on_max_change(change):
+ pbar_max.value = str(change['new'])
+
+ def _on_val_change(change):
+ pbar_current.value = str(change['new'])
+
+ pbar.observe(_on_max_change, names='max')
+ pbar.observe(_on_val_change, names='value')
+
+ jobs_widget = widgets.HBox([pbar_current, pbar, pbar_max],
+ layout=widgets.Layout(max_width='250px',
+ min_width='250px',
+ justify_content='center'))
+
+ return jobs_widget
diff --git a/qiskit/tools/jupyter/jupyter_magics.py b/qiskit/tools/jupyter/jupyter_magics.py
index 61658623263c..13322952646f 100644
--- a/qiskit/tools/jupyter/jupyter_magics.py
+++ b/qiskit/tools/jupyter/jupyter_magics.py
@@ -14,7 +14,8 @@
from IPython.core.magic import cell_magic, Magics, magics_class # pylint: disable=import-error
import ipywidgets as widgets # pylint: disable=import-error
import qiskit
-from .progressbar import HTMLProgressBar, TextProgressBar
+from qiskit.transpiler.progressbar import TextProgressBar
+from .progressbar import HTMLProgressBar
def _html_checker(job_var, interval, status, header):
diff --git a/qiskit/tools/monitor/__init__.py b/qiskit/tools/monitor/__init__.py
new file mode 100644
index 000000000000..cb0f88081af1
--- /dev/null
+++ b/qiskit/tools/monitor/__init__.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2018, IBM.
+#
+# This source code is licensed under the Apache License, Version 2.0 found in
+# the LICENSE.txt file in the root directory of this source tree.
+
+# pylint: disable=redefined-builtin
+
+"""A module for monitoring jobs, backends, etc.
+"""
+
+from .job_monitor import job_monitor
+from .backend_overview import backend_monitor, backend_overview
diff --git a/qiskit/tools/monitor/backend_overview.py b/qiskit/tools/monitor/backend_overview.py
new file mode 100644
index 000000000000..9a4dd116f894
--- /dev/null
+++ b/qiskit/tools/monitor/backend_overview.py
@@ -0,0 +1,186 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2018, IBM.
+#
+# This source code is licensed under the Apache License, Version 2.0 found in
+# the LICENSE.txt file in the root directory of this source tree.
+
+""" A module for viewing the details of all available devices.
+"""
+
+import math
+from qiskit.qiskiterror import QISKitError
+from qiskit.backends.ibmq import IBMQ
+from qiskit.backends.ibmq.ibmqbackend import IBMQBackend
+
+
+def get_unique_backends():
+ """Gets the unique backends that are available.
+
+ Returns:
+ list: Unique available backends.
+
+ Raises:
+ QISKitError: No backends available.
+ """
+ backends = IBMQ.backends()
+ unique_hardware_backends = []
+ unique_names = []
+ for back in backends:
+ if back.name() not in unique_names and not back.configuration().simulator:
+ unique_hardware_backends.append(back)
+ unique_names.append(back.name())
+ if not unique_hardware_backends:
+ raise QISKitError('No backends available.')
+ return unique_hardware_backends
+
+
+def backend_monitor(backend):
+ """Monitor a single IBMQ backend.
+
+ Args:
+ backend (IBMQBackend): Backend to monitor.
+ Raises:
+ QISKitError: Input is not a IBMQ backend.
+ """
+ if not isinstance(backend, IBMQBackend):
+ raise QISKitError('Input variable is not of type IBMQBackend.')
+ config = backend.configuration().to_dict()
+ status = backend.status().to_dict()
+ config_dict = {**status, **config}
+ if not config['simulator']:
+ props = backend.properties().to_dict()
+
+ print(backend.name())
+ print('='*len(backend.name()))
+ print('Configuration')
+ print('-'*13)
+ offset = ' '
+
+ upper_list = ['n_qubits', 'operational',
+ 'status_msg', 'pending_jobs',
+ 'basis_gates', 'local', 'simulator']
+
+ lower_list = list(set(config_dict.keys()).difference(upper_list))
+ # Remove gates because they are in a different tab
+ lower_list.remove('gates')
+ for item in upper_list+lower_list:
+ print(offset+item+':', config_dict[item])
+
+ # Stop here if simulator
+ if config['simulator']:
+ return
+
+ print()
+ qubit_header = 'Qubits [Name / Freq / T1 / T2 / U1 err / U2 err / U3 err / Readout err]'
+ print(qubit_header)
+ print('-'*len(qubit_header))
+
+ sep = ' / '
+ for qub in range(len(props['qubits'])):
+ name = 'Q%s' % qub
+ qubit_data = props['qubits'][qub]
+ gate_data = props['gates'][3*qub:3*qub+3]
+ t1_info = qubit_data[0]
+ t2_info = qubit_data[1]
+ freq_info = qubit_data[2]
+ readout_info = qubit_data[3]
+
+ freq = str(round(freq_info['value'], 5))+' '+freq_info['unit']
+ T1 = str(round(t1_info['value'], # pylint: disable=invalid-name
+ 5))+' ' + t1_info['unit']
+ T2 = str(round(t2_info['value'], # pylint: disable=invalid-name
+ 5))+' ' + t2_info['unit']
+ # pylint: disable=invalid-name
+ U1 = str(round(gate_data[0]['parameters'][0]['value'], 5))
+ # pylint: disable=invalid-name
+ U2 = str(round(gate_data[1]['parameters'][0]['value'], 5))
+ # pylint: disable=invalid-name
+ U3 = str(round(gate_data[2]['parameters'][0]['value'], 5))
+
+ readout_error = str(round(readout_info['value'], 5))
+
+ qstr = sep.join([name, freq, T1, T2, U1, U2, U3, readout_error])
+ print(offset+qstr)
+
+ print()
+ multi_qubit_gates = props['gates'][3*config['n_qubits']:]
+ multi_header = 'Multi-Qubit Gates [Name / Type / Gate Error]'
+ print(multi_header)
+ print('-'*len(multi_header))
+
+ for gate in multi_qubit_gates:
+ name = gate['name']
+ ttype = gate['gate']
+ error = str(round(gate['parameters'][0]['value'], 5))
+ mstr = sep.join([name, ttype, error])
+ print(offset+mstr)
+
+
+def backend_overview():
+ """Gives overview information on all the IBMQ
+ backends that are available.
+ """
+ unique_hardware_backends = get_unique_backends()
+ _backends = []
+ # Sort backends by operational or not
+ for idx, back in enumerate(unique_hardware_backends):
+ if back.status().operational:
+ _backends = [back] + _backends
+ else:
+ _backends = _backends + [back]
+
+ stati = [back.status() for back in _backends]
+ idx = list(range(len(_backends)))
+ pending = [s.pending_jobs for s in stati]
+ _, least_idx = zip(*sorted(zip(pending, idx)))
+
+ # Make sure least pending is operational
+ for ind in least_idx:
+ if stati[ind].operational:
+ least_pending_idx = ind
+ break
+
+ num_rows = math.ceil(len(_backends)/3)
+
+ count = 0
+ num_backends = len(_backends)
+ for _ in range(num_rows):
+ max_len = 0
+ str_list = ['']*8
+ for idx in range(3):
+ offset = ' ' * 10 if idx else ''
+ config = _backends[count].configuration().to_dict()
+ props = _backends[count].properties().to_dict()
+ n_qubits = config['n_qubits']
+ str_list[0] += (' '*(max_len-len(str_list[0]))+offset)
+ str_list[0] += _backends[count].name()
+
+ str_list[1] += (' '*(max_len-len(str_list[1]))+offset)
+ str_list[1] += '-'*len(_backends[count].name())
+
+ str_list[2] += (' '*(max_len-len(str_list[2]))+offset)
+ str_list[2] += 'Num. Qubits: %s' % config['n_qubits']
+
+ str_list[3] += (' '*(max_len-len(str_list[3]))+offset)
+ str_list[3] += 'Pending Jobs: %s' % stati[count].pending_jobs
+
+ str_list[4] += (' '*(max_len-len(str_list[4]))+offset)
+ str_list[4] += 'Least busy: %s' % (True if count == least_pending_idx else False)
+
+ str_list[5] += (' '*(max_len-len(str_list[5]))+offset)
+ str_list[5] += 'Operational: %s' % stati[count].operational
+
+ str_list[6] += (' '*(max_len-len(str_list[6]))+offset)
+ str_list[6] += 'Avg. T1: %s' % round(sum([q[0]['value']
+ for q in props['qubits']])/n_qubits, 1)
+ str_list[7] += (' '*(max_len-len(str_list[7]))+offset)
+ str_list[7] += 'Avg. T2: %s' % round(sum([q[1]['value']
+ for q in props['qubits']])/n_qubits, 1)
+ count += 1
+ if count == num_backends:
+ break
+ max_len = max([len(s) for s in str_list])
+
+ print("\n".join(str_list))
+ print('\n'*2)
diff --git a/qiskit/tools/monitor.py b/qiskit/tools/monitor/job_monitor.py
similarity index 100%
rename from qiskit/tools/monitor.py
rename to qiskit/tools/monitor/job_monitor.py
diff --git a/qiskit/tools/visualization/__init__.py b/qiskit/tools/visualization/__init__.py
index a1460b3167ee..480f0415c271 100644
--- a/qiskit/tools/visualization/__init__.py
+++ b/qiskit/tools/visualization/__init__.py
@@ -34,3 +34,6 @@
iplot_state_hinton,
iplot_histogram,
iplot_state_paulivec)
+
+if HAS_MATPLOTLIB:
+ from ._gate_map import plot_gate_map
diff --git a/qiskit/tools/visualization/_gate_map.py b/qiskit/tools/visualization/_gate_map.py
new file mode 100644
index 000000000000..f70da07debb5
--- /dev/null
+++ b/qiskit/tools/visualization/_gate_map.py
@@ -0,0 +1,206 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2018, IBM.
+#
+# This source code is licensed under the Apache License, Version 2.0 found in
+# the LICENSE.txt file in the root directory of this source tree.
+
+"""A module for visualizing device coupling maps"""
+
+import matplotlib.pyplot as plt
+import matplotlib.patches as mpatches
+from qiskit.qiskiterror import QISKitError
+
+
+class _GraphDist():
+ """Transform the circles properly for non-square axes.
+ """
+ def __init__(self, size, ax, x=True):
+ self.size = size
+ self.ax = ax # pylint: disable=invalid-name
+ self.x = x
+
+ @property
+ def dist_real(self):
+ """Compute distance.
+ """
+ x0, y0 = self.ax.transAxes.transform( # pylint: disable=invalid-name
+ (0, 0))
+ x1, y1 = self.ax.transAxes.transform( # pylint: disable=invalid-name
+ (1, 1))
+ value = x1 - x0 if self.x else y1 - y0
+ return value
+
+ @property
+ def dist_abs(self):
+ """Distance abs
+ """
+ bounds = self.ax.get_xlim() if self.x else self.ax.get_ylim()
+ return bounds[0] - bounds[1]
+
+ @property
+ def value(self):
+ """Return value.
+ """
+ return (self.size / self.dist_real) * self.dist_abs
+
+ def __mul__(self, obj):
+ return self.value * obj
+
+
+def plot_gate_map(backend, figsize=None,
+ plot_directed=False,
+ label_qubits=True,
+ qubit_size=24,
+ line_width=4,
+ font_size=12,
+ qubit_color=None,
+ line_color=None,
+ font_color='w'):
+ """Plots the gate map of a device.
+
+ Args:
+ backend (BaseBackend): A backend instance,
+ figsize (tuple): Output figure size (wxh) in inches.
+ plot_directed (bool): Plot directed coupling map.
+ label_qubits (bool): Label the qubits.
+ qubit_size (float): Size of qubit marker.
+ line_width (float): Width of lines.
+ font_size (int): Font size of qubit labels.
+ qubit_color (list): A list of colors for the qubits
+ line_color (list): A list of colors for each line from coupling_map.
+ font_color (str): The font color for the qubit labels.
+
+ Returns:
+ Figure: A Matplotlib figure instance.
+
+ Raises:
+ QISKitError: Tried to pass a simulator.
+ """
+ if backend.configuration().simulator:
+ raise QISKitError('Requires a device backend, not simulator.')
+
+ mpl_data = {}
+
+ mpl_data['ibmq_20_tokyo'] = [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4],
+ [1, 0], [1, 1], [1, 2], [1, 3], [1, 4],
+ [2, 0], [2, 1], [2, 2], [2, 3], [2, 4],
+ [3, 0], [3, 1], [3, 2], [3, 3], [3, 4]]
+
+ mpl_data['ibmq_poughkeepsie'] = mpl_data['ibmq_20_tokyo']
+
+ mpl_data['ibmq_16_melbourne'] = [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4],
+ [0, 5], [0, 6], [1, 7], [1, 6], [1, 5],
+ [1, 4], [1, 3], [1, 2], [1, 1]]
+
+ mpl_data['ibmq_16_rueschlikon'] = [[1, 0], [0, 0], [0, 1], [0, 2], [0, 3],
+ [0, 4], [0, 5], [0, 6], [0, 7], [1, 7],
+ [1, 6], [1, 5], [1, 4], [1, 3], [1, 2], [1, 1]]
+
+ mpl_data['ibmq_5_tenerife'] = [[1, 0], [0, 1], [1, 1], [1, 2], [2, 1]]
+
+ mpl_data['ibmq_5_yorktown'] = mpl_data['ibmq_5_tenerife']
+
+ config = backend.configuration()
+ name = config.backend_name
+ cmap = config.coupling_map
+
+ dep_names = {'ibmqx5': 'ibmq_16_rueschlikon',
+ 'ibmqx4': 'ibmq_5_tenerife',
+ 'ibmqx2': 'ibmq_5_yorktown'}
+
+ if name in dep_names.keys():
+ name = dep_names[name]
+
+ if name in mpl_data.keys():
+ grid_data = mpl_data[name]
+ else:
+ fig, ax = plt.subplots(figsize=(5, 5)) # pylint: disable=invalid-name
+ ax.axis('off')
+ return fig
+
+ 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)
+
+ if figsize is None:
+ if x_max/max_dim > 0.33 and y_max/max_dim > 0.33:
+ figsize = (5, 5)
+ else:
+ figsize = (9, 3)
+
+ fig, ax = plt.subplots(figsize=figsize) # pylint: disable=invalid-name
+ ax.axis('off')
+ fig.tight_layout()
+
+ # set coloring
+ if qubit_color is None:
+ qubit_color = ['#648fff']*config.n_qubits
+ if line_color is None:
+ line_color = ['#648fff']*len(cmap)
+
+ # Add lines for couplings
+ for ind, edge in enumerate(cmap):
+ is_symmetric = False
+ if edge[::-1] in cmap:
+ is_symmetric = True
+ y_start = grid_data[edge[0]][0]
+ x_start = grid_data[edge[0]][1]
+ y_end = grid_data[edge[1]][0]
+ x_end = grid_data[edge[1]][1]
+
+ if is_symmetric:
+ if y_start == y_end:
+ x_end = (x_end - x_start)/2+x_start
+
+ elif x_start == x_end:
+ y_end = (y_end - y_start)/2+y_start
+
+ else:
+ x_end = (x_end - x_start)/2+x_start
+ y_end = (y_end - y_start)/2+y_start
+ ax.add_artist(plt.Line2D([x_start, x_end], [-y_start, -y_end],
+ color=line_color[ind], linewidth=line_width,
+ zorder=0))
+ if plot_directed:
+ dx = x_end-x_start # pylint: disable=invalid-name
+ dy = y_end-y_start # pylint: disable=invalid-name
+ if is_symmetric:
+ x_arrow = x_start+dx*0.95
+ y_arrow = -y_start-dy*0.95
+ dx_arrow = dx*0.01
+ dy_arrow = -dy*0.01
+ head_width = 0.15
+ else:
+ x_arrow = x_start+dx*0.5
+ y_arrow = -y_start-dy*0.5
+ dx_arrow = dx*0.2
+ dy_arrow = -dy*0.2
+ head_width = 0.2
+ ax.add_patch(mpatches.FancyArrow(x_arrow,
+ y_arrow,
+ dx_arrow,
+ dy_arrow,
+ head_width=head_width,
+ length_includes_head=True,
+ edgecolor=None,
+ linewidth=0,
+ facecolor=line_color[ind],
+ zorder=1))
+
+ # Add circles for qubits
+ for var, idx in enumerate(grid_data):
+ _idx = [idx[1], -idx[0]]
+ width = _GraphDist(qubit_size, ax, True)
+ height = _GraphDist(qubit_size, ax, False)
+ ax.add_artist(mpatches.Ellipse(
+ _idx, width, height, color=qubit_color[var], zorder=1))
+ if label_qubits:
+ ax.text(*_idx, s=str(var),
+ horizontalalignment='center',
+ verticalalignment='center',
+ color=font_color, size=font_size, weight='bold')
+ ax.set_xlim([-1, x_max+1])
+ ax.set_ylim([-(y_max+1), 1])
+ plt.close(fig)
+ return fig
diff --git a/qiskit/transpiler/progressbar.py b/qiskit/transpiler/progressbar.py
new file mode 100644
index 000000000000..5837c2c6661a
--- /dev/null
+++ b/qiskit/transpiler/progressbar.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2018, IBM.
+#
+# This source code is licensed under the Apache License, Version 2.0 found in
+# the LICENSE.txt file in the root directory of this source tree.
+
+# This file is part of QuTiP: Quantum Toolbox in Python.
+#
+# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names
+# of its contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+###############################################################################
+
+"""Progress bars module"""
+
+import time
+import datetime
+import sys
+from qiskit._pubsub import Subscriber
+
+
+class BaseProgressBar(Subscriber):
+ """An abstract progress bar with some shared functionality.
+ """
+ def __init__(self):
+ super().__init__()
+ self.type = 'progressbar'
+ self.touched = False
+ self.iter = None
+ self.t_start = None
+ self.t_done = None
+
+ def start(self, iterations):
+ """Start the progress bar.
+
+ Parameters:
+ iterations (int): Number of iterations.
+ """
+ self.touched = True
+ self.iter = int(iterations)
+ self.t_start = time.time()
+
+ def update(self, n):
+ """Update status of progress bar.
+ """
+ pass
+
+ def time_elapsed(self):
+ """Return the time elapsed since start.
+
+ Returns:
+ elapsed_time: Time since progress bar started.
+ """
+ return "%6.2fs" % (time.time() - self.t_start)
+
+ def time_remaining_est(self, completed_iter):
+ """Estimate the remaining time left.
+
+ Parameters:
+ completed_iter (int): Number of iterations completed.
+
+ Returns:
+ est_time: Estimated time remaining.
+ """
+ if completed_iter:
+ t_r_est = (time.time() - self.t_start) / \
+ completed_iter*(self.iter-completed_iter)
+ else:
+ t_r_est = 0
+ date_time = datetime.datetime(1, 1, 1) + datetime.timedelta(seconds=t_r_est)
+ time_string = "%02d:%02d:%02d:%02d" % \
+ (date_time.day - 1, date_time.hour, date_time.minute, date_time.second)
+
+ return time_string
+
+ def finished(self):
+ """Run when progress bar has completed.
+ """
+ pass
+
+
+class TextProgressBar(BaseProgressBar):
+ """
+ A simple text-based progress bar.
+ """
+
+ def __init__(self):
+ super().__init__()
+ self._init_subscriber()
+
+ def _init_subscriber(self):
+ def _initialize_progress_bar(num_tasks):
+ """ """
+ self.start(num_tasks)
+ self.subscribe("terra.transpiler.transpile_dag.start", _initialize_progress_bar)
+
+ def _update_progress_bar(progress):
+ """ """
+ self.update(progress)
+ self.subscribe("terra.transpiler.transpile_dag.done", _update_progress_bar)
+
+ def _finish_progress_bar():
+ """ """
+ self.unsubscribe("terra.transpiler.transpile_dag.start", _initialize_progress_bar)
+ self.unsubscribe("terra.transpiler.transpile_dag.done", _update_progress_bar)
+ self.unsubscribe("terra.transpiler.transpile_dag.finish", _finish_progress_bar)
+ self.finished()
+ self.subscribe("terra.transpiler.transpile_dag.finish", _finish_progress_bar)
+
+ def start(self, iterations):
+ self.touched = True
+ self.iter = int(iterations)
+ self.t_start = time.time()
+ pbar = '-' * 50
+ sys.stdout.write('\r|%s| %s%s%s [%s]' %
+ (pbar, 0, '/', self.iter, ''))
+
+ def update(self, n):
+ filled_length = int(round(50 * n / self.iter))
+ pbar = u'█' * filled_length + '-' * (50 - filled_length)
+ time_left = self.time_remaining_est(n)
+ sys.stdout.write('\r|%s| %s%s%s [%s]' % (pbar, n, '/', self.iter, time_left))
+ if n == self.iter:
+ sys.stdout.write('\n')
+ sys.stdout.flush()
diff --git a/test/python/notebooks/test_backend_tools.ipynb b/test/python/notebooks/test_backend_tools.ipynb
new file mode 100644
index 000000000000..b2355a923939
--- /dev/null
+++ b/test/python/notebooks/test_backend_tools.ipynb
@@ -0,0 +1,117 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import sys\n",
+ "cwd = os.getcwd()\n",
+ "qiskit_dir = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(cwd))))\n",
+ "sys.path.append(qiskit_dir)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from qiskit import IBMQ\n",
+ "from qiskit.qiskiterror import QISKitError\n",
+ "from qiskit.tools.jupyter import *"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "IBMQ.load_accounts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "backends = IBMQ.backends()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "my_backend = None\n",
+ "for back in backends:\n",
+ " if not back.configuration().simulator and back.status().operational:\n",
+ " my_backend = back\n",
+ " break\n",
+ "if my_backend is None:\n",
+ " raise QISKitError('Could not load an operational IBMQ device backend.')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%qiskit_backend_monitor my_backend"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "%qiskit_backend_overview"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "hide_input": false,
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/test/python/notebooks/test_pbar_status.ipynb b/test/python/notebooks/test_pbar_status.ipynb
index b61200375350..daa56e4f74c4 100644
--- a/test/python/notebooks/test_pbar_status.ipynb
+++ b/test/python/notebooks/test_pbar_status.ipynb
@@ -13,7 +13,6 @@
"source": [
"import os\n",
"import sys\n",
- "import time\n",
"cwd = os.getcwd()\n",
"qiskit_dir = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(cwd))))\n",
"sys.path.append(qiskit_dir)"
@@ -32,7 +31,8 @@
"source": [
"from qiskit import LegacySimulators, QuantumRegister, ClassicalRegister, QuantumCircuit, execute\n",
"from qiskit.transpiler._parallel import parallel_map\n",
- "from qiskit.tools import job_monitor\n",
+ "from qiskit.transpiler.progressbar import TextProgressBar\n",
+ "from qiskit.tools.monitor import job_monitor\n",
"from qiskit.tools.jupyter import *"
]
},
@@ -47,6 +47,7 @@
},
"outputs": [],
"source": [
+ "import time\n",
"def func(_):\n",
" time.sleep(0.1)\n",
" return 0"
@@ -188,11 +189,11 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 9,
"metadata": {
"ExecuteTime": {
- "end_time": "2018-11-08T10:45:49.348875Z",
- "start_time": "2018-11-08T10:45:46.637049Z"
+ "end_time": "2018-11-19T05:09:14.423001Z",
+ "start_time": "2018-11-19T05:09:11.998829Z"
}
},
"outputs": [
@@ -213,11 +214,11 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 10,
"metadata": {
"ExecuteTime": {
- "end_time": "2018-11-08T10:45:51.781515Z",
- "start_time": "2018-11-08T10:45:49.351455Z"
+ "end_time": "2018-11-19T05:09:17.109977Z",
+ "start_time": "2018-11-19T05:09:14.426453Z"
}
},
"outputs": [
@@ -262,6 +263,9 @@
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.0"
+ },
+ "nteract": {
+ "version": "0.12.3"
}
},
"nbformat": 4,
diff --git a/test/python/tools/jupyter/test_notebooks.py b/test/python/tools/jupyter/test_notebooks.py
index 8d1dbd012ab0..0bf8602139f4 100644
--- a/test/python/tools/jupyter/test_notebooks.py
+++ b/test/python/tools/jupyter/test_notebooks.py
@@ -14,8 +14,9 @@
import nbformat
from nbconvert.preprocessors import ExecutePreprocessor
-
-from ...common import Path, QiskitTestCase, requires_cpp_simulator
+from qiskit.tools.visualization._matplotlib import HAS_MATPLOTLIB
+from ...common import (Path, QiskitTestCase, requires_qe_access,
+ requires_cpp_simulator)
# Timeout (in seconds) for a single notebook.
@@ -27,11 +28,9 @@
class TestJupyter(QiskitTestCase):
"""Notebooks test case."""
def setUp(self):
- self.filename = self._get_resource_path(
- 'notebooks/test_pbar_status.ipynb')
self.execution_path = os.path.join(Path.SDK.value, '..')
- def _execute_notebook(self, filename):
+ def _execute_notebook(self, filename, qe_token=None, qe_url=None):
# Create the preprocessor.
execute_preprocessor = ExecutePreprocessor(timeout=TIMEOUT,
kernel_name=JUPYTER_KERNEL)
@@ -40,14 +39,35 @@ def _execute_notebook(self, filename):
with open(filename) as file_:
notebook = nbformat.read(file_, as_version=4)
+ if qe_token and qe_url:
+ top_str = "from qiskit import IBMQ\n"
+ top_str += "IBMQ.enable_account('{token}', '{url}')".format(token=qe_token,
+ url=qe_url)
+ top = nbformat.notebooknode.NotebookNode({'cell_type': 'code',
+ 'execution_count': 0,
+ 'metadata': {},
+ 'outputs': [],
+ 'source': top_str})
+ notebook.cells = [top] + notebook.cells
+
# Run the notebook into the folder containing the `qiskit/` module.
execute_preprocessor.preprocess(
notebook, {'metadata': {'path': self.execution_path}})
@requires_cpp_simulator
- def test_jupyter(self):
- "Test Jupyter functionality"
- self._execute_notebook(self.filename)
+ def test_jupyter_jobs_pbars(self):
+ "Test Jupyter progress bars and job status functionality"
+ self._execute_notebook(self._get_resource_path(
+ 'notebooks/test_pbar_status.ipynb'))
+
+ @unittest.skipIf(not HAS_MATPLOTLIB, 'matplotlib not available.')
+ @requires_qe_access
+ def test_backend_tools(self, qe_token, qe_url):
+ "Test Jupyter backend tools."
+ self._execute_notebook(self._get_resource_path(
+ 'notebooks/test_backend_tools.ipynb'),
+ qe_token=qe_token,
+ qe_url=qe_url)
if __name__ == '__main__':
diff --git a/test/python/tools/monitor/test_backend_monitor.py b/test/python/tools/monitor/test_backend_monitor.py
new file mode 100644
index 000000000000..335335f3ac97
--- /dev/null
+++ b/test/python/tools/monitor/test_backend_monitor.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2018, IBM.
+#
+# This source code is licensed under the Apache License, Version 2.0 found in
+# the LICENSE.txt file in the root directory of this source tree.
+
+# pylint: disable=redefined-builtin
+
+"""Tests for the wrapper functionality."""
+
+import unittest
+from qiskit.backends.ibmq import IBMQ
+from qiskit.tools.monitor import backend_overview, backend_monitor
+from ...common import QiskitTestCase, requires_qe_access
+
+
+class TestBackendOverview(QiskitTestCase):
+ """Tools test case."""
+ @requires_qe_access
+ def test_backend_overview(self, qe_token, qe_url):
+ """Test backend_overview"""
+ IBMQ.enable_account(qe_token, qe_url)
+ backend_overview()
+
+ def test_backend_monitor(self, qe_token, qe_url):
+ """Test backend_monitor"""
+ IBMQ.enable_account(qe_token, qe_url)
+ for back in IBMQ.backends():
+ if not back.configuration().simulator:
+ backend = back
+ break
+ backend_monitor(backend)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/test/python/tools/test_job_monitor.py b/test/python/tools/monitor/test_job_monitor.py
similarity index 89%
rename from test/python/tools/test_job_monitor.py
rename to test/python/tools/monitor/test_job_monitor.py
index a9cfbbf9c32a..98d85269691e 100644
--- a/test/python/tools/test_job_monitor.py
+++ b/test/python/tools/monitor/test_job_monitor.py
@@ -10,12 +10,11 @@
"""Tests for the wrapper functionality."""
import unittest
-
-import qiskit.tools
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit import BasicAer
from qiskit import execute
-from ..common import QiskitTestCase
+from qiskit.tools.monitor import job_monitor
+from ...common import QiskitTestCase
class TestJobMonitor(QiskitTestCase):
@@ -30,7 +29,7 @@ def test_job_monitor(self):
qc.measure(qreg, creg)
backend = BasicAer.get_backend('qasm_simulator')
job_sim = execute([qc]*10, backend)
- qiskit.tools.job_monitor(job_sim)
+ job_monitor(job_sim)
self.assertEqual(job_sim.status().name, 'DONE')