diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3ee7d0b173cc..71cf72c5787f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -43,6 +43,15 @@ Changed - Breaking change: ``Jobs`` API simplification. (#686) - Breaking change: altered tomography APIs to not use QuantumProgram. (#818) - Breaking change: ``BaseBackend`` API changed, properties are now methods (#858) +- When ``plot_histogram()`` or ``plot_state()`` are called from a jupyter + notebook if there is network connectivity the interactive plots will be used + by default (#862, #866) + +Deprecated +---------- +- The ``number_to_keep`` kwarg on the ``plot_histogram()`` function is now + deprecated. A field of the same name should be used in the ``option`` + dictionary kwarg instead. (#866) Removed ------- diff --git a/qiskit/tools/visualization/__init__.py b/qiskit/tools/visualization/__init__.py index ed291bbaf232..acee26bd58cb 100644 --- a/qiskit/tools/visualization/__init__.py +++ b/qiskit/tools/visualization/__init__.py @@ -11,15 +11,21 @@ from ._circuit_visualization import circuit_drawer, plot_circuit, generate_latex_source,\ latex_circuit_drawer, matplotlib_circuit_drawer, qx_color_scheme +from ._error import VisualizationError from ._state_visualization import plot_bloch_vector -from ._counts_visualization import plot_histogram + if ('ipykernel' in sys.modules) and ('spyder' not in sys.modules): import requests if requests.get( 'https://qvisualization.mybluemix.net/').status_code == 200: from .interactive._iplot_state import iplot_state as plot_state + from .interactive._iplot_histogram import iplot_histogram as \ + plot_histogram else: from ._state_visualization import plot_state + from ._counts_visualization import plot_histogram + else: from ._state_visualization import plot_state + from ._counts_visualization import plot_histogram diff --git a/qiskit/tools/visualization/_counts_visualization.py b/qiskit/tools/visualization/_counts_visualization.py index e7af0f22a2ac..6aac7fcf2596 100644 --- a/qiskit/tools/visualization/_counts_visualization.py +++ b/qiskit/tools/visualization/_counts_visualization.py @@ -12,40 +12,79 @@ """ from collections import Counter +import warnings import numpy as np import matplotlib.pyplot as plt +from ._error import VisualizationError -def plot_histogram(data, number_to_keep=False): + +def plot_histogram(data, number_to_keep=False, legend=None, options=None): """Plot a histogram of data. - data is a dictionary of {'000': 5, '010': 113, ...} - number_to_keep is the number of terms to plot and rest is made into a - single bar called other values + Args: + data (list or dict): This is either a list of dictionaries or a single + dict containing the values to represent (ex {'001': 130}) + number_to_keep (int): DEPRECATED the number of terms to plot and rest + is made into a single bar called other values + legend(list): A list of strings to use for labels of the data. + The number of entries must match the lenght of data (if data is a + list or 1 if it's a dict) + options (dict): Representation settings containing + - number_to_keep (integer): groups max values + - show_legend (bool): show legend of graph content + Raises: + VisualizationError: When legend is provided and the length doesn't + match the input data. """ + if options is None: + options = {} + if number_to_keep is not False: - data_temp = dict(Counter(data).most_common(number_to_keep)) - data_temp["rest"] = sum(data.values()) - sum(data_temp.values()) - data = data_temp - - labels = sorted(data) - values = np.array([data[key] for key in labels], dtype=float) - pvalues = values / sum(values) - numelem = len(values) - ind = np.arange(numelem) # the x locations for the groups - width = 0.35 # the width of the bars + warnings.warn("number_to_keep has been deprecated, use the options " + "dictionary and set a number_to_keep key instead", + DeprecationWarning) + + if isinstance(data, dict): + data = [data] + + if legend and len(legend) != len(data): + raise VisualizationError("Length of legendL (%s) doesn't match " + "number of input executions: %s" % + (len(legend), len(data))) + _, ax = plt.subplots() - rects = ax.bar(ind, pvalues, width, color='seagreen') - # add some text for labels, title, and axes ticks - ax.set_ylabel('Probabilities', fontsize=12) - ax.set_xticks(ind) - ax.set_xticklabels(labels, fontsize=12, rotation=70) - ax.set_ylim([0., min([1.2, max([1.2 * val for val in pvalues])])]) - # attach some text labels - for rect in rects: - height = rect.get_height() - ax.text(rect.get_x() + rect.get_width() / 2., 1.05 * height, - '%f' % float(height), - ha='center', va='bottom') + for item, execution in enumerate(data): + if number_to_keep is not False or ( + 'number_to_keep' in options and options['number_to_keep']): + data_temp = dict(Counter(execution).most_common(number_to_keep)) + data_temp["rest"] = sum(execution.values()) - sum(data_temp.values()) + execution = data_temp + + labels = sorted(execution) + values = np.array([execution[key] for key in labels], dtype=float) + pvalues = values / sum(values) + numelem = len(values) + ind = np.arange(numelem) # the x locations for the groups + width = 0.35 # the width of the bars + label = None + if legend: + label = legend[item] + adj = width * item + rects = ax.bar(ind+adj, pvalues, width, label=label) + # add some text for labels, title, and axes ticks + ax.set_ylabel('Probabilities', fontsize=12) + ax.set_xticks(ind) + ax.set_xticklabels(labels, fontsize=12, rotation=70) + ax.set_ylim([0., min([1.2, max([1.2 * val for val in pvalues])])]) + # attach some text labels + for rect in rects: + height = rect.get_height() + ax.text(rect.get_x() + rect.get_width() / 2., 1.05 * height, + '%f' % float(height), + ha='center', va='bottom') + if legend and ( + 'show_legend' not in options or options['show_legend'] is True): + plt.legend() plt.show() diff --git a/qiskit/tools/visualization/_error.py b/qiskit/tools/visualization/_error.py new file mode 100644 index 000000000000..21dcf7be1dbf --- /dev/null +++ b/qiskit/tools/visualization/_error.py @@ -0,0 +1,15 @@ +# -*- 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. + +"""QISKit visualization error classes.""" + +from qiskit import QISKitError + + +class VisualizationError(QISKitError): + """For visualization specific errors.""" + pass diff --git a/qiskit/tools/visualization/interactive/_iplot_histogram.py b/qiskit/tools/visualization/interactive/_iplot_histogram.py index b6495a631b9a..43aa34f8d72a 100644 --- a/qiskit/tools/visualization/interactive/_iplot_histogram.py +++ b/qiskit/tools/visualization/interactive/_iplot_histogram.py @@ -14,6 +14,8 @@ import time import re import numpy as np +from .._error import VisualizationError + if ('ipykernel' in sys.modules) and ('spyder' not in sys.modules): try: from IPython.core.display import display, HTML @@ -48,17 +50,19 @@ def process_data(data, number_to_keep): return result -def iplot_histogram(executions_results, options=None): +def iplot_histogram(data, number_to_keep=False, options=None, legend=None): """ Create a histogram representation. Graphical representation of the input array using a vertical bars style graph. Args: - executions_results (array): Array of dictionaries containing - - data (dict): values to represent (ex. {'001' : 130}) - - name (string): name to show in the legend - - device (string): Could be 'real' or 'simulated' + data (list or dict): This is either a list of dicts or a single + dict containing the values to represent (ex. {'001' : 130}) + number_to_keep (int): DEPRECATED the number of terms to plot and + rest is made into a single bar called other values + legend (list): A list of strings to use for labels of the data. + The number of entries must match the length of data. options (dict): Representation settings containing - width (integer): graph horizontal size - height (integer): graph vertical size @@ -66,6 +70,9 @@ def iplot_histogram(executions_results, options=None): - number_to_keep (integer): groups max values - show_legend (bool): show legend of graph content - sort (string): Could be 'asc' or 'desc' + Raises: + VisualizationError: When legend is provided and the length doesn't + match the input data. """ # HTML @@ -111,12 +118,26 @@ def iplot_histogram(executions_results, options=None): options['show_legend'] = 1 if 'number_to_keep' not in options: - options['number_to_keep'] = 0 + if number_to_keep is False: + options['number_to_keep'] = 0 + elif number_to_keep: + options['number_to_keep'] = number_to_keep data_to_plot = [] - for execution in executions_results: - data = process_data(execution['data'], options['number_to_keep']) - data_to_plot.append({'data': data}) + if isinstance(data, dict): + data = [data] + + if legend and len(legend) != len(data): + raise VisualizationError("Length of legendL (%s) doesn't match number " + "of input executions: %s" % + (len(legend), len(data))) + + for item, execution in enumerate(data): + exec_data = process_data(execution, options['number_to_keep']) + out_dict = {'data': exec_data} + if legend: + out_dict['name'] = legend[item] + data_to_plot.append(out_dict) html = html_template.substitute({ 'divNumber': div_number