Skip to content

Commit

Permalink
get jobs from IBM Q backend (Qiskit#501)
Browse files Browse the repository at this point in the history
* add jobs() method to retrieve all jobs from a backend
* job cancellation improvements for hubs
* add retrieve_job() method for ibmq backends
* add circuit names to non-qobj ibmq job submission
* remove qobj from Result
* add temporary hack for bit reordering
* allow retrieving old jobs without metadata
* property "creationDate" was added to ibmq jobs.
  • Loading branch information
ewinston authored and atilag committed Jun 20, 2018
1 parent 31b3baa commit d1bc545
Show file tree
Hide file tree
Showing 18 changed files with 428 additions and 190 deletions.
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 = {
"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

0 comments on commit d1bc545

Please sign in to comment.