Skip to main content
IBM Quantum Platform
Información sobre la traducción

Próximamente habrá traducciones a otros idiomas de esta página.

Define inputs and outputs

A managed workflow works like a Python function that runs on remote compute. Compare a local function with the same code as a workflow:

Local Python
def my_workflow(circuits, shots=4096):
    ...
    return {"counts": counts}

result = my_workflow(circuits=circuits, shots=1024)

In a managed workflow, run() is the call, get_arguments() gives the workflow its parameters, and save_result() is its return statement. Because the workflow runs as a separate job on remote compute, it differs from a local function call in three ways:

  • The client encodes arguments and results. Each value must be one that the client can encode, and some types change. See Read arguments in your workflow.
  • The call does not wait. run() returns a job immediately, and you read the result later with job.result(). See Save results.
  • Nothing checks the call. A Python function rejects an unknown keyword argument, but a workflow ignores it unless you attach a schema. See Best practice: validate arguments with a schema.
Your local notebook, run_workflow_remotely.ipynb, calls run(args). A workflow runs an optional schema check, then creates the managed workflow job, source_files/my_workflow.py. The job reads its arguments with get_arguments(), runs your code, such as mapping, transpiling circuits, running primitives, and post-processing, and returns a result with save_result(). A workflow stores the result, and your notebook reads it with job.result().

Read arguments in your workflow

Pass inputs to your workflow from your local notebook:

run_workflow_remotely.ipynb
job = my_workflow.run(circuits=circuits, shots=1024)

To enable this, edit your workflow code to fetch arguments with get_arguments(), which returns a dictionary whose keys are the argument names:

source_files/my_workflow.py
from qiskit_serverless import get_arguments

arguments = get_arguments()
circuits = arguments["circuits"]
shots = arguments.get("shots", 4096)

The dictionary contains only the arguments that the run passed, so use arguments.get() with a default value for optional arguments. You can use any argument name except the protected keywords config and function_size, because run() uses these keywords directly. run() also rejects compute_profile. To choose compute resources, pass function_size.

To read the size that the job runs at, call get_function_size() in your workflow code:

source_files/my_workflow.py
from qiskit_serverless import get_function_size

size = get_function_size()  # For example, "M"

If the run does not pass function_size, get_function_size() returns the workflow's default size. See Choose a size for each run.

Supported argument types

Each value must be JSON-serializable, or an object that the client can encode, such as a Qiskit circuit or a NumPy array. Because the client encodes each argument before it sends the job and get_arguments() decodes it inside the workflow, the workflow receives the original Python object.

The client sends int, float, str, bool, None, and lists or dictionaries of these unchanged. It encodes other objects, such as a QuantumCircuit, a numpy.ndarray, or a complex number, as a dictionary with a __type__ tag and a __value__. You need this form when you write an argument schema.

Because JSON has no equivalent for some types, these types change during encoding. Tuples arrive as lists, dictionary keys arrive as strings, and NumPy integers such as np.int64 arrive as int. If the client cannot encode a value, such as bytes or np.float32, run() raises a TypeError and does not create a job.

Some objects cannot be passed as arguments. For example, pass a backend's name as a string instead of a Backend object, and create the backend inside the workflow. See Run quantum jobs from a workflow.

Size limits

The arguments of one run can be up to 32 MB after encoding. For example, one 100-qubit circuit with a depth of 100 encodes to about 39 KB, so a single run can take a batch of about 800 such circuits. For larger inputs, upload a file and pass its name as an argument.


Save results

Use save_result() at the end of your workflow to return a result to your notebook. Read the result in your notebook with job.result():

run_workflow_remotely.ipynb
result = job.result()

To enable this, call save_result() in your workflow code:

source_files/my_workflow.py
from qiskit_serverless import save_result

if not save_result({"counts": counts, "circuit": isa_circuit}):
    raise ValueError("The result could not be encoded.")

save_result() encodes the result in the same way that the client encodes arguments, and job.result() decodes it. A result can therefore contain the supported argument types, such as circuits, NumPy arrays, and the result of a primitive job, with the same type changes.

  • Check the return value. If save_result() cannot encode the value, it logs a warning and returns False instead of raising an error. Without a check, the job can end with the DONE status and no result.
  • Only the last save_result() is stored. Each call replaces the saved result. To return several values, save them in one dictionary, or write them to the logs.
  • An empty dictionary means no result. If the workflow never calls save_result(), job.result() returns {}.

To return files, such as large arrays or plots, save them to file storage and return their names in the result.


Best practice: validate arguments with a schema

As a best practice, attach a JSON Schema to every workflow that takes arguments. The schema describes the arguments that the workflow accepts, and the workflow checks the arguments of every run against it before it creates the job. If the arguments do not match, run() fails immediately with an error that names the bad field, and the workflow does not create a job. Without a schema, bad arguments reach the workflow after the job starts, and the workflow ignores a misspelled argument name.

Set the schema with arguments_schema when you upload the workflow. Pass a dictionary, not a JSON string:

run_workflow_remotely.ipynb
from qiskit_ibm_catalog import ManagedWorkflow, ManagedWorkflowClient

arguments_schema = {
    "type": "object",
    "additionalProperties": False,
    "required": ["circuits"],
    "properties": {
        "circuits": {
            "type": "array",
            "minItems": 1,
            "items": {"$ref": "#/$defs/circuit"},
        },
        "shots": {"type": "integer", "minimum": 1, "maximum": 100000},
        "backend_name": {"type": "string", "minLength": 1},
    },
    "$defs": {
        "circuit": {
            "type": "object",
            "required": ["__type__", "__value__"],
            "properties": {
                "__type__": {"const": "QuantumCircuit"},
                "__value__": {"type": "string"},
            },
        }
    },
}

client = ManagedWorkflowClient()

my_workflow = client.upload(
    ManagedWorkflow(
        title="my-workflow",
        entrypoint="my_workflow.py",
        working_dir="./source_files/",
        arguments_schema=arguments_schema,
    )
)

Keep the following points in mind when you write a schema:

  • Describe the encoded form of Qiskit and NumPy objects. The schema checks the arguments as the client sends them, so a circuit is an object such as {"__type__": "QuantumCircuit", "__value__": "<base64-encoded QPY>"}, not a string or an array. Check the __type__ tag, and treat __value__ as an opaque string. A schema that describes a circuit as a string rejects every valid run.
  • Set "additionalProperties": false. Without this setting, the workflow ignores a misspelled argument name, such as shotz, without a warning, and runs with its default value. Declare every argument that the workflow accepts before you enable this setting.
  • Mark arguments as required. Add each argument that the workflow reads without a default value to required.

If the arguments do not match the schema, the error message names the rule and the location of the bad value:

'type' validation failed, schema requires 'integer': rejected value '4096'
path: ['shots']

A schema checks the type, shape, and range of each argument. A schema cannot check whether two valid arguments conflict, or whether a circuit fits a backend. Keep those checks in the workflow code.

Update or remove a schema

  • To replace the schema, set a new arguments_schema and upload the workflow again.
  • To remove the schema, upload the workflow with arguments_schema={}.
  • If you upload the workflow without arguments_schema, the workflow keeps the stored schema.

To see the schema that is in effect, read my_workflow.arguments_schema. A value of None means that the workflow has no schema.

Check arguments before you run

Call validate_arguments() to check a set of arguments against the schema without starting a job:

run_workflow_remotely.ipynb
my_workflow.validate_arguments({"circuits": [circuit], "shots": 1024})
# {'valid': True}

If the arguments are not valid, validate_arguments() raises a QiskitServerlessException with the same message that run() returns for those arguments. If the workflow has no schema, any arguments are valid. A valid result does not guarantee that run() succeeds, because run() also checks other limits, such as the number of active jobs.

Schema rules

A managed workflow rejects the upload if the schema breaks any of the following rules:

  • The schema can be up to 64 KiB, with up to 64 levels of nesting and up to 200 subschemas.
  • References must point inside the same schema, such as #/$defs/circuit, and every reference must resolve. References to URLs or files are not allowed.
  • The schema must be an object or a boolean.

Also avoid the following problems, which the upload does not catch:

  • Nested quantifiers in pattern. A pattern such as ^(a+)+$ can take a very long time to evaluate. A workflow stops validation after a CPU time limit and rejects the run. Write patterns without a quantifier around a group that already contains one, such as ^ibm_[a-z0-9_]{1,40}$.
  • false as a schema. The schema false rejects every run. To remove validation, use {}.

Troubleshoot a schema

Problem
Cause
Every run is rejected on a circuit argument.The schema describes the circuit as a string or an array. Describe it as an object with a __type__ tag.
Valid runs are rejected.additionalProperties is false, and the schema does not declare an argument that the run passes. The error path names the argument.
Invalid runs are not rejected.The workflow has no schema. Check my_workflow.arguments_schema.
No run is checked, although you set a schema.The schema was passed as a JSON string. Pass a dictionary.
The upload fails with a 400 error.The schema breaks one of the schema rules.
The error says that validation took too much CPU time.A pattern has nested quantifiers. Rewrite the pattern.
¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.