Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conform iplot_histogram and plot_histogram #866

Merged
merged 6 commits into from
Sep 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------
Expand Down
8 changes: 7 additions & 1 deletion qiskit/tools/visualization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
91 changes: 65 additions & 26 deletions qiskit/tools/visualization/_counts_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: do you think is it feasible or even useful to allow customization of graph width, graph height and sort to equate the options dictionary passed to this function to that supported by the iplot_function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be, and I thought about that when I was refactoring things. But I didn't want to change things too much here. After this merges I'll work on updating this to add the missing options.

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()
15 changes: 15 additions & 0 deletions qiskit/tools/visualization/_error.py
Original file line number Diff line number Diff line change
@@ -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
39 changes: 30 additions & 9 deletions qiskit/tools/visualization/interactive/_iplot_histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -48,24 +50,29 @@ 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
- slider (bool): activate slider
- 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
Expand Down Expand Up @@ -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
Expand Down