Saltar al contenido principal
Este proyecto está en fase beta. ¿Encontraste un error? Abre un issue en GitHub. ¿Tienes una pregunta o una idea? Pregunta en Discussions — ¡nos encantaría tu opinión!
¿Cómo es la calidad de la traducción en esta página?

Definir entradas y salidas

Un managed workflow funciona como una función de Python que se ejecuta en cómputo remoto. Compara una función local con el mismo código como workflow:

Local Python

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

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

En un managed workflow, run() es la llamada, get_arguments() le da al workflow sus parámetros y save_result() es su instrucción return. Como el workflow se ejecuta como un trabajo independiente en cómputo remoto, se diferencia de una llamada a una función local en tres aspectos:

  • El cliente codifica los argumentos y los resultados. Cada valor debe ser uno que el cliente pueda codificar, y algunos tipos cambian. Consulta Leer argumentos en tu workflow.

  • La llamada no espera. run() devuelve un trabajo de inmediato, y lees el resultado más tarde con job.result(). Consulta Guardar resultados.

  • Nada comprueba la llamada. Una función de Python rechaza un argumento de palabra clave desconocido, pero un workflow lo ignora a menos que adjuntes un esquema. Consulta Práctica recomendada: valida los argumentos con un esquema.

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().

Leer argumentos en tu workflow​

Pasa entradas a tu workflow desde tu notebook local:

run_workflow_remotely.ipynb

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

Para habilitar esto, edita el código de tu workflow para obtener los argumentos con get_arguments(), que devuelve un diccionario cuyas claves son los nombres de los argumentos:

source_files/my_workflow.py

from qiskit_serverless import get_arguments

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

El diccionario contiene solo los argumentos que pasó la ejecución, así que usa arguments.get() con un valor predeterminado para los argumentos opcionales. Puedes usar cualquier nombre de argumento excepto las palabras clave protegidas config y function_size, porque run() usa estas palabras clave directamente. run() también rechaza compute_profile. Para elegir los recursos de cómputo, pasa function_size.

Para leer el tamaño con el que se ejecuta el trabajo, llama a get_function_size() en el código de tu workflow:

source_files/my_workflow.py

from qiskit_serverless import get_function_size

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

Si la ejecución no pasa function_size, get_function_size() devuelve el tamaño predeterminado del workflow. Consulta Elegir un tamaño para cada ejecución.

Tipos de argumentos admitidos​

Cada valor debe ser serializable en JSON, o un objeto que el cliente pueda codificar, como un circuito de Qiskit o un arreglo de NumPy. Como el cliente codifica cada argumento antes de enviar el trabajo y get_arguments() lo decodifica dentro del workflow, el workflow recibe el objeto de Python original.

El cliente envía sin cambios int, float, str, bool, None, y listas o diccionarios de estos tipos. Codifica otros objetos, como un QuantumCircuit, un numpy.ndarray o un número complex, como un diccionario con una etiqueta __type__ y un __value__. Necesitas esta forma cuando escribes un esquema de argumentos.

Como JSON no tiene un equivalente para algunos tipos, estos tipos cambian durante la codificación. Las tuplas llegan como listas, las claves de los diccionarios llegan como cadenas y los enteros de NumPy, como np.int64, llegan como int. Si el cliente no puede codificar un valor, como bytes o np.float32, run() lanza un TypeError y no crea un trabajo.

Algunos objetos no se pueden pasar como argumentos. Por ejemplo, pasa el nombre de un backend como cadena en lugar de un objeto Backend, y crea el backend dentro del workflow. Consulta Ejecutar trabajos cuánticos desde un workflow.

Límites de tamaño​

Los argumentos de una ejecución pueden ocupar hasta 32 MB después de la codificación. Por ejemplo, un circuito de 100 qubits con una profundidad de 100 se codifica en unos 39 KB, por lo que una sola ejecución puede recibir un lote de unos 800 circuitos de ese tipo. Para entradas más grandes, sube un archivo y pasa su nombre como argumento.

Guardar resultados​

Usa save_result() al final de tu workflow para devolver un resultado a tu notebook. Lee el resultado en tu notebook con job.result():

run_workflow_remotely.ipynb

result = job.result()

Para habilitar esto, llama a save_result() en el código de tu workflow:

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() codifica el resultado de la misma manera en que el cliente codifica los argumentos, y job.result() lo decodifica. Por lo tanto, un resultado puede contener los tipos de argumentos admitidos, como circuitos, arreglos de NumPy y el resultado de un trabajo de primitive, con los mismos cambios de tipo.

  • Comprueba el valor devuelto. Si save_result() no puede codificar el valor, registra una advertencia y devuelve False en lugar de lanzar un error. Sin esa comprobación, el trabajo puede terminar con el estado DONE y sin resultado.

  • Solo se almacena el último save_result(). Cada llamada reemplaza el resultado guardado. Para devolver varios valores, guárdalos en un solo diccionario o escríbelos en los registros.

  • Un diccionario vacío significa que no hay resultado. Si el workflow nunca llama a save_result(), job.result() devuelve {}.

Para devolver archivos, como arreglos grandes o gráficas, guárdalos en el almacenamiento de archivos y devuelve sus nombres en el resultado.

Práctica recomendada: valida los argumentos con un esquema​

Como práctica recomendada, adjunta un esquema JSON a cada workflow que reciba argumentos. El esquema describe los argumentos que acepta el workflow, y el workflow comprueba con él los argumentos de cada ejecución antes de crear el trabajo. Si los argumentos no coinciden, run() falla de inmediato con un error que nombra el campo incorrecto, y el workflow no crea un trabajo. Sin un esquema, los argumentos incorrectos llegan al workflow después de que el trabajo comienza, y el workflow ignora un nombre de argumento mal escrito.

Define el esquema con arguments_schema al subir el workflow. Pasa un diccionario, no una cadena JSON:

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,
)
)

Ten en cuenta los siguientes puntos al escribir un esquema:

  • Describe la forma codificada de los objetos de Qiskit y NumPy. El esquema comprueba los argumentos tal como los envía el cliente, por lo que un circuito es un objeto como {"__type__": "QuantumCircuit", "__value__": "<base64-encoded QPY>"}, no una cadena ni un arreglo. Comprueba la etiqueta __type__ y trata __value__ como una cadena opaca. Un esquema que describe un circuito como una cadena rechaza todas las ejecuciones válidas.

  • Define "additionalProperties": false. Sin esta configuración, el workflow ignora sin advertencia un nombre de argumento mal escrito, como shotz, y se ejecuta con su valor predeterminado. Declara todos los argumentos que acepta el workflow antes de activar esta configuración.

  • Marca los argumentos como obligatorios. Añade a required cada argumento que el workflow lea sin un valor predeterminado.

Si los argumentos no coinciden con el esquema, el mensaje de error nombra la regla y la ubicación del valor incorrecto:

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

Un esquema comprueba el tipo, la forma y el rango de cada argumento. Un esquema no puede comprobar si dos argumentos válidos entran en conflicto, ni si un circuito cabe en un backend. Mantén esas comprobaciones en el código del workflow.

Actualizar o eliminar un esquema​

  • Para reemplazar el esquema, define un nuevo arguments_schema y sube el workflow de nuevo.

  • Para eliminar el esquema, sube el workflow con arguments_schema={}.

  • Si subes el workflow sin arguments_schema, el workflow conserva el esquema almacenado.

Para ver el esquema que está en vigor, lee my_workflow.arguments_schema. Un valor de None significa que el workflow no tiene esquema.

Comprobar los argumentos antes de ejecutar​

Llama a validate_arguments() para comprobar un conjunto de argumentos con el esquema sin iniciar un trabajo:

run_workflow_remotely.ipynb

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

Si los argumentos no son válidos, validate_arguments() lanza una QiskitServerlessException con el mismo mensaje que run() devuelve para esos argumentos. Si el workflow no tiene esquema, cualquier argumento es válido. Un resultado válido no garantiza que run() tenga éxito, porque run() también comprueba otros límites, como el número de trabajos activos.

Reglas del esquema​

Un managed workflow rechaza la subida si el esquema infringe alguna de las siguientes reglas:

  • El esquema puede ocupar hasta 64 KiB, con hasta 64 niveles de anidamiento y hasta 200 subesquemas.

  • Las referencias deben apuntar al interior del mismo esquema, como #/$defs/circuit, y toda referencia debe resolverse. No se permiten referencias a URL ni a archivos.

  • El esquema debe ser un objeto o un booleano.

Evita también los siguientes problemas, que la subida no detecta:

  • Cuantificadores anidados en pattern. Un patrón como ^(a+)+$ puede tardar muchísimo en evaluarse. Un workflow detiene la validación tras un límite de tiempo de CPU y rechaza la ejecución. Escribe patrones sin un cuantificador alrededor de un grupo que ya contiene uno, como ^ibm_[a-z0-9_]{1,40}$.

  • false como esquema. El esquema false rechaza todas las ejecuciones. Para quitar la validación, usa {}.

Solucionar problemas de un esquema​

ProblemaCausa
Se rechaza toda ejecución por un argumento de circuito.El esquema describe el circuito como una cadena o un arreglo. Descríbelo como un objeto con una etiqueta __type__.
Se rechazan ejecuciones válidas.additionalProperties es false, y el esquema no declara un argumento que pasa la ejecución. El path del error nombra el argumento.
No se rechazan ejecuciones no válidas.El workflow no tiene esquema. Comprueba my_workflow.arguments_schema.
No se comprueba ninguna ejecución, aunque definiste un esquema.El esquema se pasó como una cadena JSON. Pasa un diccionario.
La subida falla con un error 400.El esquema infringe una de las reglas del esquema.
El error dice que la validación consumió demasiado tiempo de CPU.Un pattern tiene cuantificadores anidados. Reescribe el patrón.