Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.
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
14 changes: 3 additions & 11 deletions qiskit_ibm/api/clients/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,7 @@ def program_create(
description: str,
max_execution_time: int,
is_public: Optional[bool] = False,
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.

Expand All @@ -67,10 +64,7 @@ def program_create(
description: Program description.
max_execution_time: Maximum execution time.
is_public: Whether the program should be public.
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 @@ -79,9 +73,7 @@ def program_create(
program_data=program_data,
name=name,
description=description, max_execution_time=max_execution_time,
is_public=is_public, 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 Down
20 changes: 4 additions & 16 deletions qiskit_ibm/api/rest/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ def create_program(
description: str,
max_execution_time: int,
is_public: Optional[bool] = False,
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.

Expand All @@ -83,10 +80,7 @@ def create_program(
description: Program description.
max_execution_time: Maximum execution time.
is_public: Whether the program should be public.
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.
Expand All @@ -98,14 +92,8 @@ def create_program(
'description': description.encode(),
'max_execution_time': max_execution_time,
'is_public': is_public}
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)
if spec is not None:
data['spec'] = json.dumps(spec)
response = self.session.post(url, data=data).json()
return response

Expand Down
109 changes: 39 additions & 70 deletions qiskit_ibm/runtime/ibm_runtime_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
import logging
from typing import Dict, Callable, Optional, Union, List, Any, Type
import json
import copy
import re

from qiskit.providers.exceptions import QiskitBackendNotFoundError
from qiskit_ibm import ibm_provider # pylint: disable=unused-import

from .runtime_job import RuntimeJob
from .runtime_program import RuntimeProgram, ProgramParameter, ProgramResult, ParameterNamespace
from .runtime_program import RuntimeProgram, ParameterNamespace
from .utils import RuntimeEncoder, RuntimeDecoder, to_base64_string
from .exceptions import (QiskitRuntimeError, RuntimeDuplicateProgramError, RuntimeProgramNotFound,
RuntimeJobNotFound)
Expand Down Expand Up @@ -182,21 +181,26 @@ def _to_program(self, response: Dict) -> RuntimeProgram:
Returns:
A ``RuntimeProgram`` instance.
"""
backend_req = json.loads(response.get('backendRequirements', '{}'))
params = json.loads(response.get('parameters', '{}')).get("doc", [])
ret_vals = json.loads(response.get('returnValues', '{}'))
interim_results = json.loads(response.get('interimResults', '{}'))
backend_requirements = {}
parameters = {}
return_values = {}
interim_results = {}
if "spec" in response:
backend_requirements = response["spec"].get('backend_requirements', {})
parameters = response["spec"].get('parameters', {})
return_values = response["spec"].get('return_values', {})
interim_results = response["spec"].get('interim_results', {})

return RuntimeProgram(program_name=response['name'],
program_id=response['id'],
description=response.get('description', ""),
parameters=params,
return_values=ret_vals,
parameters=parameters,
return_values=return_values,
interim_results=interim_results,
max_execution_time=response.get('cost', 0),
creation_date=response.get('creation_date', ""),
update_date=response.get('update_date', ""),
backend_requirements=backend_req,
backend_requirements=backend_requirements,
is_public=response.get('is_public', False))

def run(
Expand Down Expand Up @@ -267,15 +271,7 @@ def run(
def upload_program(
self,
data: str,
metadata: Optional[Union[Dict, str]] = None,
name: Optional[str] = None,
is_public: Optional[bool] = False,
max_execution_time: Optional[int] = None,
description: Optional[str] = None,
backend_requirements: Optional[str] = None,
parameters: Optional[List[ProgramParameter]] = None,
return_values: Optional[List[ProgramResult]] = None,
interim_results: Optional[List[ProgramResult]] = None
metadata: Optional[Union[Dict, str]] = None
) -> str:
"""Upload a runtime program.

Expand All @@ -298,17 +294,23 @@ def upload_program(
Args:
data: Program data or path of the file containing program data to upload.
metadata: Name of the program metadata file or metadata dictionary.
A metadata file needs to be in the JSON format.
See :file:`program/program_metadata_sample.yaml` for an example.
name: Name of the program. Required if not specified via `metadata`.
max_execution_time: Maximum execution time in seconds. Required if
not specified via `metadata`.
is_public: Whether the runtime program should be visible to the public.
description: Program description. Required if not specified via `metadata`.
backend_requirements: Backend requirements.
parameters: A list of program input parameters.
return_values: A list of program return values.
interim_results: A list of program interim results.
A metadata file needs to be in the JSON format. The ``parameters``,
``return_values``, and ``interim_results`` should be defined as JSON Schema.
See :file:`program/program_metadata_sample.json` for an example. The
fields in metadata are explained below.

* name: Name of the program. Required.
* max_execution_time: Maximum execution time in seconds. Required.
* description: Program description. Required.
* is_public: Whether the runtime program should be visible to the public.
Comment thread
rathishcholarajan marked this conversation as resolved.
The default is ``False``.
* spec: Specifications for backend characteristics and input parameters
required to run the program, interim results and final result.

* backend_requirements: Backend requirements.
* parameters: Program input parameters in JSON schema format.
* return_values: Program return values in JSON schema format.
* interim_results: Program interim results in JSON schema format.

Returns:
Program ID.
Expand All @@ -319,14 +321,7 @@ def upload_program(
IBMNotAuthorizedError: If you are not authorized to upload programs.
QiskitRuntimeError: If the upload failed.
"""
program_metadata = self._merge_metadata(
initial={},
metadata=metadata,
name=name, max_execution_time=max_execution_time,
is_public=is_public, description=description,
backend_requirements=backend_requirements,
parameters=parameters,
return_values=return_values, interim_results=interim_results)
program_metadata = self._read_metadata(metadata=metadata)

for req in ['name', 'description', 'max_execution_time']:
if req not in program_metadata or not program_metadata[req]:
Expand All @@ -351,55 +346,29 @@ def upload_program(
raise QiskitRuntimeError(f"Failed to create program: {ex}") from None
return response['id']

def _merge_metadata(
def _read_metadata(
self,
initial: Dict,
metadata: Optional[Union[Dict, str]] = None,
**kwargs: Any
metadata: Optional[Union[Dict, str]] = None
) -> Dict:
"""Merge multiple copies of metadata.
"""Read metadata.

Args:
initial: The initial metadata. This may be mutated.
metadata: Name of the program metadata file or metadata dictionary.
**kwargs: Additional metadata fields to overwrite.

Returns:
Merged metadata.
Return metadata.
"""
upd_metadata: dict = {}
if metadata is not None:
if isinstance(metadata, str):
with open(metadata, 'r') as file:
upd_metadata = json.load(file)
else:
upd_metadata = copy.deepcopy(metadata)

self._tuple_to_dict(initial)
initial.update(upd_metadata)

self._tuple_to_dict(kwargs)
for key, val in kwargs.items():
if val is not None:
initial[key] = val

upd_metadata = metadata
# TODO validate metadata format
metadata_keys = ['name', 'max_execution_time', 'description',
'backend_requirements', 'parameters', 'return_values',
'interim_results', 'is_public']
return {key: val for key, val in initial.items() if key in metadata_keys}

def _tuple_to_dict(self, metadata: Dict) -> None:
"""Convert fields in metadata from named tuples to dictionaries.

Args:
metadata: Metadata to be converted.
"""
for key in ['parameters', 'return_values', 'interim_results']:
doc_list = metadata.pop(key, None)
if not doc_list or isinstance(doc_list[0], dict):
continue
metadata[key] = [dict(elem._asdict()) for elem in doc_list]
'spec', 'is_public']
return {key: val for key, val in upd_metadata.items() if key in metadata_keys}

def update_program(
self,
Expand Down
45 changes: 34 additions & 11 deletions qiskit_ibm/runtime/program/program_metadata_sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,38 @@
"name": "runtime-simple",
"description": "Simple runtime program used for testing.",
"max_execution_time": 300,
"backend_requirements": {"min_num_qubits": 5},
"parameters": [
{"name": "iterations", "description": "Number of iterations to run. Each iteration generates a runs a random circuit.", "type": "integer", "required": true}
],
"return_values": [
{"name": "-", "description": "A string that says 'All done!'.", "type": "string"}
],
"interim_results": [
{"name": "iteration", "description": "Iteration number.", "type": "int"},
{"name": "counts", "description": "Histogram data of the circuit result.", "type": "dict"}
]
"spec": {
"backend_requirements": {
"min_num_qubits": 5
},
"parameters": {
"type": "object",
"properties": {
"iterations": {
"description": "Number of iterations to run. Each iteration generates and runs a random circuit.",
"type": "integer"
}
},
"required": [
"iterations"
]
},
Comment thread
rathishcholarajan marked this conversation as resolved.
"return_values": {
"type": "string",
"description": "A string that says 'All done!'."
},
"interim_results": {
"type": "object",
"properties": {
"iteration": {
"description": "Iteration number.",
"type": "integer"
},
"counts": {
"description": "Histogram data of the circuit result.",
"type": "object"
}
}
}
}
}
Loading