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

get jobs from IBM Q backend #501

Merged
merged 32 commits into from
Jun 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
bcac889
job cancellation improvements for hubs
ewinston May 18, 2018
cd825f4
add alternate ibmqjob init method; from_api()
ewinston May 9, 2018
93aa378
add retrieve_job for ibmq backends
ewinston May 21, 2018
3df6680
add name to pre-qobj job submission
ewinston May 29, 2018
e00a1b5
fix bug in ibmq job submission
ewinston May 30, 2018
a49ea94
add circuit names to non-qobj ibmq job submission
ewinston May 30, 2018
5909a0d
remove qobj from result
ewinston May 31, 2018
f4e9898
add temporary hack for bit reordering
ewinston Jun 1, 2018
5a77bd9
remove qobj keyword from ibmqjob.result()
ewinston Jun 5, 2018
641becc
changed job timer.
ewinston Jun 6, 2018
8284926
linting
ewinston Jun 6, 2018
a72d9ec
simplify import of JobStatus
ewinston Jun 7, 2018
f437e0a
dissallow += results from different backends
ewinston Jun 11, 2018
3d5904e
moved JobStatus to it's own file.
ewinston Jun 11, 2018
c9fb95f
bug fix
ewinston Jun 11, 2018
97abf82
linting
ewinston Jun 11, 2018
0afb90d
fix bug in backend.jobs
ewinston Jun 13, 2018
7516e7a
allow retrieving old jobs without metadata
ewinston Jun 13, 2018
674af67
minor improvement to backend.jobs
ewinston Jun 13, 2018
2b79d6b
linting
ewinston Jun 13, 2018
0cfd4cb
speed up backend.jobs(). job_id->id
ewinston Jun 14, 2018
00cd596
linting
ewinston Jun 14, 2018
d7d1a4f
incorporating some suggestions from @atilag
ewinston Jun 15, 2018
10d0bc2
improvements to ibmqjob.status
ewinston Jun 15, 2018
2e72457
linting
ewinston Jun 15, 2018
49edb41
linting
ewinston Jun 15, 2018
8581dcb
linting
ewinston Jun 15, 2018
11d859b
update job status_msg
ewinston Jun 15, 2018
a426d1b
fix hanging job.status on server error
ewinston Jun 16, 2018
c2d1033
remove job status filtering
ewinston Jun 18, 2018
cee799c
linting
ewinston Jun 18, 2018
13274e6
Merge branch 'master' into backend_jobs
atilag Jun 19, 2018
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
58 changes: 22 additions & 36 deletions qiskit/_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class Result(object):

Internal::

qobj = { -- the quantum object that was complied --}
result = {
Copy link
Member

Choose a reason for hiding this comment

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

I think now that Result._qobj is removed, we can unpack Result._result, and not have so many levels of indirection. That means having Result._job_id, Result._status, Result._result, where the latter contains all the data.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This sounds ok to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we can leave this for a subsequent PR since it's not directly related to this one?

Copy link
Member

Choose a reason for hiding this comment

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

yeah i think we should leave this, also because it's not clear how qobj and result may be represented together later.
I think the most important thing in this PR is to not give errors when metadata is unavailable from a past job.

"job_id": --job-id (string),
#This string links the result with the job that computes it,
Expand All @@ -45,9 +44,8 @@ class Result(object):
}
"""

def __init__(self, qobj_result, qobj):
def __init__(self, qobj_result):
self._result = qobj_result
self._qobj = qobj

def __str__(self):
"""Get the status of the run.
Expand All @@ -73,30 +71,20 @@ def __iadd__(self, other):
Raises:
QISKitError: if the Results cannot be combined.
"""
# TODO: reevaluate if moving equality to Backend themselves (part of
# todo: reevaluate if moving equality to Backend themselves (part of
# a bigger problem - backend instances will not persist between
# sessions)
my_config = copy.deepcopy(self._qobj['config'])
other_config = copy.deepcopy(other._qobj['config'])
my_backend = my_config.pop('backend_name')
other_backend = other_config.pop('backend_name')

if my_config == other_config and my_backend == other_backend:
if isinstance(self._qobj['id'], str):
self._qobj['id'] = [self._qobj['id']]
self._qobj['id'].append(other._qobj['id'])
self._qobj['circuits'] += other._qobj['circuits']
this_backend = self._result.get('backend_name')
other_backend = other._result.get('backend_name')
if this_backend == other_backend:
self._result['result'] += other._result['result']
return self
else:
raise QISKitError('Result objects have different configs and cannot be combined.')
raise QISKitError('Result objects from different backends cannot be combined.')

def __add__(self, other):
"""Combine Result objects.

Note that the qobj id of the returned result will be the same as the
first result.

Arg:
other (Result): a Result object to combine.
Returns:
Expand All @@ -110,7 +98,7 @@ def _is_error(self):
return self._result['status'] == 'ERROR'

def get_status(self):
"""Return whole qobj result status."""
"""Return whole result status."""
return self._result['status']

def circuit_statuses(self):
Expand Down Expand Up @@ -152,10 +140,9 @@ def get_ran_qasm(self, name):
QISKitError: if the circuit was not found.
"""
try:
qobj = self._qobj
for index in range(len(qobj["circuits"])):
if qobj["circuits"][index]['name'] == name:
return qobj["circuits"][index]["compiled_circuit_qasm"]
for exp_result in self._result['result']:
if exp_result.get('name') == name:
return exp_result['compiled_circuit_qasm']
except KeyError:
pass
raise QISKitError('No qasm for circuit "{0}"'.format(name))
Expand Down Expand Up @@ -211,20 +198,18 @@ def get_data(self, circuit=None):
circuit = circuit.name

if circuit is None:
circuits = list([i['name'] for i in self._qobj['circuits']])
if len(circuits) == 1:
circuit = circuits[0]
if len(self._result['result']) == 1:
return self._result['result'][0]['data']
else:
raise QISKitError("You have to select a circuit when there is more than"
"one available")

if not isinstance(circuit, str):
circuit = str(circuit)
try:
qobj = self._qobj
for index in range(len(qobj['circuits'])):
if qobj['circuits'][index]['name'] == circuit:
return self._result['result'][index]['data']
for circuit_result in self._result['result']:
if circuit_result.get('name') == circuit:
return circuit_result['data']
except (KeyError, TypeError):
pass
raise QISKitError('No data for circuit "{0}"'.format(circuit))
Expand Down Expand Up @@ -365,7 +350,7 @@ def get_names(self):
Returns:
List: A list of circuit names.
"""
return [c['name'] for c in self._qobj['circuits']]
return [c.get('name') for c in self._result['result']]

def average_data(self, name, observable):
"""Compute the mean value of an diagonal observable.
Expand All @@ -390,22 +375,22 @@ def average_data(self, name, observable):
temp += counts[key] * observable[key] / tot
return temp

def get_qubitpol_vs_xval(self, xvals_dict=None):
def get_qubitpol_vs_xval(self, nqubits, xvals_dict=None):
"""Compute the polarization of each qubit for all circuits and pull out each circuits
xval into an array. Assumes that each circuit has the same number of qubits and that
all qubits are measured.

Args:
nqubits (int): number of qubits
xvals_dict (dict): xvals for each circuit {'circuitname1': xval1,...}. If this
is none then the xvals list is just left as an array of zeros

Returns:
qubit_pol: mxn double array where m is the number of circuit, n the number of qubits
xvals: mx1 array of the circuit xvals
"""
ncircuits = len(self._qobj['circuits'])
ncircuits = len(self._result['result'])
# Is this the best way to get the number of qubits?
nqubits = self._qobj['circuits'][0]['compiled_circuit']['header']['number_of_qubits']
qubitpol = numpy.zeros([ncircuits, nqubits], dtype=float)
xvals = numpy.zeros([ncircuits], dtype=float)

Expand All @@ -421,10 +406,11 @@ def get_qubitpol_vs_xval(self, xvals_dict=None):

# go through each circuit and for eqch qubit and apply the operators using "average_data"
for circuit_ind in range(ncircuits):
circuit_name = self._result['result'][circuit_ind]['name']
if xvals_dict:
xvals[circuit_ind] = xvals_dict[self._qobj['circuits'][circuit_ind]['name']]
xvals[circuit_ind] = xvals_dict[circuit_name]
for qubit_ind in range(nqubits):
qubitpol[circuit_ind, qubit_ind] = self.average_data(
self._qobj['circuits'][circuit_ind]['name'], z_dicts[qubit_ind])
circuit_name, z_dicts[qubit_ind])

return qubitpol, xvals
1 change: 1 addition & 0 deletions qiskit/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
from .basebackend import BaseBackend
from .baseprovider import BaseProvider
from .basejob import BaseJob
from .jobstatus import JobStatus
11 changes: 0 additions & 11 deletions qiskit/backends/basejob.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"""

from abc import ABC, abstractmethod
import enum


class BaseJob(ABC):
Expand Down Expand Up @@ -58,13 +57,3 @@ def done(self):
def cancelled(self):
"""True if call was successfully cancelled"""
pass


class JobStatus(enum.Enum):
"""Class for job status enumerated type."""
INITIALIZING = 'job is being initialized'
QUEUED = 'job is queued'
RUNNING = 'job is actively running'
CANCELLED = 'job has been cancelled'
DONE = 'job has successfully run'
ERROR = 'job incurred error'
44 changes: 44 additions & 0 deletions qiskit/backends/ibmq/ibmqbackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"""
import logging

from qiskit import QISKitError
from qiskit._util import _camel_case_to_snake_case
from qiskit.backends import BaseBackend
from qiskit.backends.ibmq.ibmqjob import IBMQJob
Expand Down Expand Up @@ -139,3 +140,46 @@ def status(self):
raise LookupError(
"Couldn't get backend status: {0}".format(ex))
return status

def jobs(self, limit=50, skip=0):
"""Attempt to get the jobs submitted to the backend

Args:
limit (int): number of jobs to retrieve
skip (int): starting index of retrieval
Returns:
list(IBMQJob): list of IBMQJob instances
"""
backend_name = self.configuration['name']
job_info_list = self._api.get_jobs(limit=limit, skip=skip,
backend=backend_name)
job_list = []
for job_info in job_info_list:
is_device = not bool(self._configuration.get('simulator'))
job = IBMQJob.from_api(job_info, self._api, is_device)
job_list.append(job)
return job_list

def retrieve_job(self, job_id):
"""Attempt to get the specified job by job_id

Args:
job_id (str): the job id of the job to retrieve

Returns:
IBMQJob: class instance

Raises:
IBMQBackendError: if retrieval failed
"""
job_info = self._api.get_job(job_id)
if 'error' in job_info:
raise IBMQBackendError('failed to get job id "{}"'.format(job_id))
is_device = not bool(self._configuration.get('simulator'))
job = IBMQJob.from_api(job_info, self._api, is_device)
return job


class IBMQBackendError(QISKitError):
"""IBM Q Backend Errors"""
pass
Loading