Skip to content
This repository was archived by the owner on Jul 28, 2023. It is now read-only.
Closed
87 changes: 51 additions & 36 deletions qiskit/providers/ibmq/api/clients/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""Client for accessing IBM Quantum runtime service."""

import logging
from typing import List, Dict, Union, Optional
from typing import Any, List, Dict, Union, Optional

from qiskit.providers.ibmq.credentials import Credentials
from qiskit.providers.ibmq.api.session import RetrySession
Expand All @@ -39,40 +39,36 @@ def __init__(
**credentials.connection_parameters())
self.api = Runtime(self._session)

def list_programs(self) -> List[Dict]:
def list_programs(self, limit: int = None, skip: int = None) -> Dict[str, Any]:
"""Return a list of runtime programs.

Args:
limit: The number of programs to return.
skip: The number of programs to skip.

Returns:
A list of quantum programs.
A list of runtime programs.
"""
return self.api.list_programs()
return self.api.list_programs(limit, skip)

def program_create(
self,
program_data: bytes,
program_data: str,
name: str,
description: str,
max_execution_time: int,
is_public: Optional[bool] = False,
version: Optional[str] = None,
backend_requirements: Optional[Dict] = None,
parameters: Optional[Dict] = None,
return_values: Optional[List] = None,
interim_results: Optional[List] = None
spec: Optional[Dict] = None
) -> Dict:
"""Create a new program.

Args:
name: Name of the program.
program_data: Program data.
program_data: Program data (base64 encoded).
description: Program description.
max_execution_time: Maximum execution time.
is_public: Whether the program should be public.
version: Program version.
backend_requirements: Backend requirements.
parameters: Program parameters.
return_values: Program return values.
interim_results: Program interim results.
spec: Backend requirements, parameters, interim results, return values, etc.

Returns:
Server response.
Expand All @@ -81,9 +77,7 @@ def program_create(
program_data=program_data,
name=name,
description=description, max_execution_time=max_execution_time,
is_public=is_public, version=version, backend_requirements=backend_requirements,
parameters=parameters, return_values=return_values,
interim_results=interim_results
is_public=is_public, spec=spec
)

def program_get(self, program_id: str) -> Dict:
Expand All @@ -97,17 +91,6 @@ def program_get(self, program_id: str) -> Dict:
"""
return self.api.program(program_id).get()

def program_get_data(self, program_id: str) -> Dict:
"""Return a specific program and its data.

Args:
program_id: Program ID.

Returns:
Program information, including data.
"""
return self.api.program(program_id).get_data()

def set_program_visibility(self, program_id: str, public: bool) -> None:
"""Sets a program's visibility.

Expand All @@ -127,7 +110,7 @@ def program_run(
program_id: str,
credentials: Credentials,
backend_name: str,
params: str,
params: Dict,
image: str
) -> Dict:
"""Run the specified program.
Expand Down Expand Up @@ -155,14 +138,32 @@ def program_delete(self, program_id: str) -> None:
"""
self.api.program(program_id).delete()

def program_update(self, program_id: str, program_data: str) -> None:
def program_update(
self,
program_id: str,
program_data: str = None,
name: str = None,
description: str = None,
max_execution_time: int = None,
spec: Optional[Dict] = None
) -> None:
"""Update a program.

Args:
program_id: Program ID.
program_data: Program data.
program_data: Program data (base64 encoded).
name: Name of the program.
description: Program description.
max_execution_time: Maximum execution time.
spec: Backend requirements, parameters, interim results, return values, etc.
"""
self.api.program(program_id).update(program_data)
if program_data:
self.api.program(program_id).update_data(program_data)

if any([name, description, max_execution_time, spec]):
self.api.program(program_id).update_metadata(
name=name, description=description,
max_execution_time=max_execution_time, spec=spec)

def job_get(self, job_id: str) -> Dict:
"""Get job data.
Expand All @@ -177,19 +178,33 @@ def job_get(self, job_id: str) -> Dict:
logger.debug("Runtime job get response: %s", response)
return response

def jobs_get(self, limit: int = None, skip: int = None, pending: bool = None) -> Dict:
def jobs_get(
self,
limit: int = None,
skip: int = None,
pending: bool = None,
program_id: str = None,
hub: str = None,
group: str = None,
project: str = None
) -> Dict:
"""Get job data for all jobs.

Args:
limit: Number of results to return.
skip: Number of results to skip.
pending: Returns 'QUEUED' and 'RUNNING' jobs if True,
returns 'DONE', 'CANCELLED' and 'ERROR' jobs if False.
program_id: Filter by Program ID.
hub: Filter by hub - hub, group, and project must all be specified.
group: Filter by group - hub, group, and project must all be specified.
project: Filter by project - hub, group, and project must all be specified.

Returns:
JSON response.
"""
return self.api.jobs_get(limit=limit, skip=skip, pending=pending)
return self.api.jobs_get(limit=limit, skip=skip, pending=pending,
program_id=program_id, hub=hub, group=group, project=project)

def job_results(self, job_id: str) -> str:
"""Get the results of a program job.
Expand Down
132 changes: 80 additions & 52 deletions qiskit/providers/ibmq/api/rest/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from .base import RestAdapterBase
from ..session import RetrySession
from ...runtime.utils import RuntimeEncoder

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -54,65 +55,56 @@ def program_job(self, job_id: str) -> 'ProgramJob':
"""
return ProgramJob(self.session, job_id)

def list_programs(self) -> List[Dict]:
def list_programs(self, limit: int = None, skip: int = None) -> Dict[str, Any]:
"""Return a list of runtime programs.

Args:
limit: The number of programs to return.
skip: The number of programs to skip.

Returns:
JSON response.
A list of runtime programs.
"""
url = self.get_url('programs')
return self.session.get(url).json()
payload: Dict[str, int] = {}
if limit:
payload['limit'] = limit
if skip:
payload['offset'] = skip
return self.session.get(url, params=payload).json()

def create_program(
self,
program_data: bytes,
program_data: str,
name: str,
description: str,
max_execution_time: int,
is_public: Optional[bool] = False,
version: Optional[str] = None,
backend_requirements: Optional[Dict] = None,
parameters: Optional[Dict] = None,
return_values: Optional[List] = None,
interim_results: Optional[List] = None
spec: Optional[Dict] = None
) -> Dict:
"""Upload a new program.

Args:
program_data: Program data.
program_data: Program data (base64 encoded).
name: Name of the program.
description: Program description.
max_execution_time: Maximum execution time.
is_public: Whether the program should be public.
version: Program version.
backend_requirements: Backend requirements.
parameters: Program parameters.
return_values: Program return values.
interim_results: Program interim results.
spec: Backend requirements, parameters, interim results, return values, etc.

Returns:
JSON response.
"""
url = self.get_url('programs')
data = {'name': name,
'cost': str(max_execution_time),
'description': description.encode(),
'max_execution_time': max_execution_time,
'isPublic': is_public}
if version is not None:
data['version'] = version
if backend_requirements:
data['backendRequirements'] = json.dumps(backend_requirements)
if parameters:
data['parameters'] = json.dumps({"doc": parameters})
if return_values:
data['returnValues'] = json.dumps(return_values)
if interim_results:
data['interimResults'] = json.dumps(interim_results)

files = {'program': (name, program_data)} # type: ignore[dict-item]
response = self.session.post(url, data=data, files=files).json()
return response
payload = {'name': name,
'data': program_data,
'cost': max_execution_time,
'description': description,
'is_public': is_public}
if spec is not None:
payload['spec'] = spec
data = json.dumps(payload)
return self.session.post(url, data=data).json()

def program_run(
self,
Expand All @@ -121,7 +113,7 @@ def program_run(
group: str,
project: str,
backend_name: str,
params: str,
params: Dict,
image: str
) -> Dict:
"""Execute the program.
Expand All @@ -140,25 +132,38 @@ def program_run(
"""
url = self.get_url('jobs')
payload = {
'programId': program_id,
'program_id': program_id,
'hub': hub,
'group': group,
'project': project,
'backend': backend_name,
'params': [params],
'params': params,
'runtime': image
}
data = json.dumps(payload)
data = json.dumps(payload, cls=RuntimeEncoder)
return self.session.post(url, data=data).json()

def jobs_get(self, limit: int = None, skip: int = None, pending: bool = None) -> Dict:
def jobs_get(
self,
limit: int = None,
skip: int = None,
pending: bool = None,
program_id: str = None,
hub: str = None,
group: str = None,
project: str = None
) -> Dict:
"""Get a list of job data.

Args:
limit: Number of results to return.
skip: Number of results to skip.
pending: Returns 'QUEUED' and 'RUNNING' jobs if True,
returns 'DONE', 'CANCELLED' and 'ERROR' jobs if False.
program_id: Filter by Program ID.
hub: Filter by hub - hub, group, and project must all be specified.
group: Filter by group - hub, group, and project must all be specified.
project: Filter by project - hub, group, and project must all be specified.

Returns:
JSON response.
Expand All @@ -171,6 +176,10 @@ def jobs_get(self, limit: int = None, skip: int = None, pending: bool = None) ->
payload['offset'] = skip
if pending is not None:
payload['pending'] = 'true' if pending else 'false'
if program_id:
payload['program'] = program_id
if all([hub, group, project]):
payload['provider'] = f"{hub}/{group}/{project}"
return self.session.get(url, params=payload).json()

def logout(self) -> None:
Expand Down Expand Up @@ -211,15 +220,6 @@ def get(self) -> Dict[str, Any]:
url = self.get_url('self')
return self.session.get(url).json()

def get_data(self) -> Dict[str, Any]:
"""Return program information, including data.

Returns:
JSON response.
"""
url = self.get_url('data')
return self.session.get(url).json()

def make_public(self) -> None:
"""Sets a runtime program's visibility to public."""
url = self.get_url('public')
Expand All @@ -239,15 +239,43 @@ def delete(self) -> None:
url = self.get_url('self')
self.session.delete(url)

def update(self, program_data: str) -> None:
"""Update a program.
def update_data(self, program_data: str) -> None:
"""Update program data.

Args:
program_data: Program data.
program_data: Program data (base64 encoded).
"""
url = self.get_url("data")
self.session.put(url, data=program_data,
headers={'Content-Type': 'text/plain'})
headers={'Content-Type': 'application/octet-stream'})

def update_metadata(
self,
name: str = None,
description: str = None,
max_execution_time: int = None,
spec: Optional[Dict] = None
) -> None:
"""Update program metadata.

Args:
name: Name of the program.
description: Program description.
max_execution_time: Maximum execution time.
spec: Backend requirements, parameters, interim results, return values, etc.
"""
url = self.get_url("self")
payload: Dict = {}
if name:
payload["name"] = name
if description:
payload["description"] = description
if max_execution_time:
payload["cost"] = max_execution_time
if spec:
payload["spec"] = spec

self.session.patch(url, json=payload)


class ProgramJob(RestAdapterBase):
Expand Down
Loading