Formas de ejecutar un programa de Q#

Uno de los puntos fuertes del kit de desarrollo de Quantum es su flexibilidad en todas las plataformas y entornos de desarrollo. Sin embargo, esta flexibilidad también significa que los nuevos usuarios de Q# pueden sentirse confundidos o abrumados por las numerosas opciones que hay en la guía de instalación. En esta página, se explica lo que ocurre cuando se ejecuta un programa de Q# y se comparan las diferentes formas de hacerlo.

Una distinción primordial es que Q# se puede ejecutar:

  • Como una aplicación independiente; Q# es el único lenguaje involucrado y el programa se invoca directamente. En realidad, hay dos métodos en esta categoría:
    • la interfaz de la línea de comandos
    • Cuadernos de Jupyter Notebook en Q#
  • con un programa host adicional, escrito en Python o en un lenguaje de .NET (por ejemplo, C# o F#), que luego invoca el programa y puede seguir procesando los resultados devueltos.

Para comprender mejor estos procesos y sus diferencias, veremos un programa de Q# y compararemos las formas de ejecutarlo.

Programa básico de Q#

Un programa cuántico básico podría consistir en preparar un cúbit en una superposición igual de estados $\ket{0}$ y $\ket{1}$, medirlo y devolver el resultado. El resultado será aleatoriamente cualquiera de estos dos estados con igual probabilidad. De hecho, este proceso es el núcleo del inicio rápido del generador cuántico de números aleatorios.

En Q#, el código siguiente realizaría la generación de números aleatorios:

        use q = Qubit();     // allocates qubit for use (automatically in |0>)
        H(q);                // puts qubit in superposition of |0> and |1>
        return MResetZ(q);   // measures qubit, returns result (and resets it to |0> before deallocation)

Sin embargo, Q# no puede ejecutar este código por sí solo. Para ejecutarlo, necesita crear el cuerpo de una operación, que luego se ejecutará cuando se la llame, ya sea directamente o mediante otra operación. Por lo tanto, se puede escribir una operación con la siguiente forma:

    operation MeasureSuperposition() : Result {
        use q = Qubit();     // allocates qubit for use (automatically in |0>)
        H(q);                // puts qubit in superposition of |0> and |1>
        return MResetZ(q);   // measures qubit, returns result (and resets it to |0> before deallocation)
    }

Ha definido una operación, MeasureSuperposition, que no toma entradas y devuelve un valor de tipo Result.

Además de las operaciones, Q# también le permite encapsular cálculos deterministas en funciones. Aparte de garantizar el determinismo, que implica que los cálculos que actúan sobre los cúbits se deben encapsular en operaciones y no en funciones, hay poca diferencia entre las operaciones y las funciones. Nos referimos a ellos colectivamente como invocables.

Invocable definido en un archivo de Q#

El invocable es precisamente lo que Q# llama y ejecuta. Sin embargo, requiere algunas adiciones más para conformar un archivo *.qs de Q# completo.

Todos los tipos e invocables de Q#, tanto los definidos por el usuario como los intrínsecos del lenguaje, se definen dentro de espacios de nombres, que proporcionan a cada uno un nombre completo al que luego se puede hacer referencia.

Por ejemplo, las operaciones H y MResetZ se encuentran en los espacios de nombres Microsoft.Quantum.Instrinsic y Microsoft.Quantum.Measurement (que forman parte de las bibliotecas estándar de Q#). Como tal, siempre se las puede llamar por sus nombres completos, Microsoft.Quantum.Intrinsic.H(<qubit>) y Microsoft.Quantum.Measurement.MResetZ(<qubit>), pero realizar siempre esta especificación completa produciría código saturado.

En su lugar, las instrucciones open permiten hacer referencia a los invocables con una forma abreviada más concisa, como se ha hecho en el cuerpo de la operación anterior. El archivo completo de Q# que contiene nuestra operación consistiría, por tanto, en definir nuestro propio espacio de nombres, abrir los espacios de nombres para los invocables que usa nuestra operación y, a continuación, la operación:

namespace Superposition {
    open Microsoft.Quantum.Intrinsic;     // for the H operation
    open Microsoft.Quantum.Measurement;   // for MResetZ

    operation MeasureSuperposition() : Result {
        use q = Qubit();     // allocates qubit for use (automatically in |0>)
        H(q);                // puts qubit in superposition of |0> and |1>
        return MResetZ(q);   // measures qubit, returns result (and resets it to |0> before deallocation)
    }
}

Nota:

También se puede usar alias al abrir los espacios de nombres, lo que puede resultar útil si los nombres de invocables o tipos de dos espacios de nombres entran en conflicto. Por ejemplo, podríamos utilizar open Microsoft.Quantum.Instrinsic as NamespaceWithH; y, después, llamar a H mediante NamespaceWithH.H(<qubit>).

Nota

Una excepción a todo esto es el espacio de nombres Microsoft.Quantum.Core, que siempre se abre automáticamente. Por lo tanto, los invocables como Length siempre pueden utilizarse directamente.

Ejecución en las máquinas de destino

Ahora ya está claro el modelo de ejecución general de un programa de Q#.


Q# program execution diagram

El invocable específico que se va a ejecutar tiene acceso a otros invocables y tipos definidos en el mismo espacio de nombres. También accede a esos elementos de cualquiera de las bibliotecas de Q#, pero se debe hacer referencia a estos elementos mediante su nombre completo o mediante las instrucciones open descritas anteriormente.

El invocable se ejecuta entonces en una máquina de destino . Estas máquinas de destino pueden ser hardware cuántico real o uno de los numerosos simuladores disponibles como parte del QDK. Para nuestros fines, la máquina de destino más útil es una instancia del simulador de estado completo, QuantumSimulator, que calcula el comportamiento del programa como si se ejecutara en un equipo cuántico sin ruido.

Hasta ahora, hemos visto lo que ocurre cuando se ejecuta un invocable de Q# específico. Al margen de si Q# se utiliza en una aplicación independiente o con un programa host, este proceso general es más o menos el mismo, de ahí la flexibilidad del QDK. Las diferencias entre las formas de llamar al kit de desarrollo de Quantum surgen, por lo tanto, en cómo se invoca a ese invocable de Q# y de qué manera se devuelven los resultados. Más concretamente, las diferencias giran en torno a:

  • Indicación de qué invocable de Q# debe ejecutarse
  • Cómo se proporcionan los posibles argumentos invocables
  • Especificación de la máquina de destino en la que se va a ejecutar
  • Cómo se devuelven los resultados

En las siguientes secciones, aprenderá cómo se hace esto con la aplicación independiente de Q# desde el símbolo del sistema. A continuación, continuará con el uso de programas host de Python y C#. Reservamos la aplicación independiente de Jupyter Notebooks de Q# para el final porque, a diferencia de las tres primeras, su funcionalidad principal no se centra en un archivo de Q# local.

Nota:

Aunque no se ilustra en estos ejemplos, un aspecto común de los métodos de ejecución es que cualquier mensaje que se imprima desde el programa de Q# (mediante Message o DumpMachine, por ejemplo) se imprimirá siempre en la consola correspondiente.

Q# desde el símbolo del sistema

Una de las formas más fáciles de empezar a escribir programas de Q# es evitar preocuparse por tener archivos separados y un segundo lenguaje. El uso de Visual Studio Code o Visual Studio con la extensión QDK permite un flujo de trabajo fluido en el que ejecutamos invocables de Q# desde un único archivo de Q#.

Para ello, ejecutará el programa especificando:

dotnet run

en el símbolo del sistema. El flujo de trabajo más sencillo es cuando la ubicación del directorio del terminal es la misma que la del archivo de Q#. Esto se puede configurar fácilmente editando el archivo de Q# en el terminal integrado en VS Code, por ejemplo. Sin embargo, el comando dotnet run acepta numerosas opciones, y el programa también se puede ejecutar desde otra ubicación proporcionando a --project <PATH> la ubicación del archivo de Q#.

Adición de un punto de entrada al archivo de Q#

La mayoría de los archivos de Q# contendrán más de un invocable, por lo que necesitamos que el compilador sepa cuál debe ejecutar cuando le demos la orden dotnet run. Esta especificación se lleva a cabo con un simple cambio en el propio archivo de Q#; debe agregar una línea con @EntryPoint() directamente antes del invocable.

El archivo anterior se convertiría en:

namespace Superposition {
    open Microsoft.Quantum.Intrinsic;     // for the H operation
    open Microsoft.Quantum.Measurement;   // for MResetZ

    @EntryPoint()
    operation MeasureSuperposition() : Result {
        use q = Qubit();     // allocates qubit for use (automatically in |0>)
        H(q);                // puts qubit in superposition of |0> and |1>
        return MResetZ(q);   // measures qubit, returns result (and resets it to |0> before deallocation)
    }
}

Ahora, una llamada de dotnet run desde el símbolo del sistema hace que se ejecute MeasureSuperposition, y el valor devuelto se imprime directamente en el terminal. Así, verá impreso One o Zero.

No importa si tiene más invocables definidos después; solo se ejecutará MeasureSuperposition. Además, no hay problema si el invocable incluye comentarios de documentación antes de la declaración; el atributo @EntryPoint() se puede colocar encima de ellos.

Argumentos invocables

Hasta ahora, en el artículo solo se ha tenido en cuenta una operación que no toma entradas. Suponga que quiere realizar una operación similar, pero sobre varios cúbits, cuyo número se proporciona como argumento. Dicha operación podría escribirse como:

namespace MultiSuperposition {
    open Microsoft.Quantum.Intrinsic;     // for the H operation
    open Microsoft.Quantum.Measurement;   // for MResetZ
    open Microsoft.Quantum.Canon;         // for ApplyToEach
    open Microsoft.Quantum.Arrays;        // for ForEach
    
    @EntryPoint()
    operation MeasureSuperpositionArray(n : Int) : Result[] {
        use qubits = Qubit[n];               // allocate a register of n qubits in |0> 
        ApplyToEach(H, qubits);              // apply H to each qubit in the register
        return ForEach(MResetZ, qubits);     // perform MResetZ on each qubit, returns the resulting array
    }
}

donde el valor devuelto es una matriz de resultados de la medida. ApplyToEach y ForEach están en los espacios de nombres Microsoft.Quantum.Canon y Microsoft.Quantum.Arrays, lo que requiere otra instrucción open para cada una.

Si se traslada el atributo @EntryPoint() para que preceda a esta nueva operación (tenga en cuenta que solo puede haber una línea de este tipo en un archivo), al intentar ejecutarla simplemente con dotnet run, aparece un mensaje de error que indica las opciones adicionales de la línea de comandos que son necesarias y cómo expresarlas.

El formato general de la línea de comandos es, en realidad, dotnet run [options], y en él se proporcionan argumentos invocables. En este caso, falta el argumento n, y muestra que tenemos que proporcionar la opción -n <n>. Para ejecutar MeasureSuperpositionArray para n=4 qubits, debe ejecutar:

dotnet run -n 4

que da una salida similar a:

[Zero,One,One,One]

Esto, por supuesto, se extiende a varios argumentos.

Nota

El compilador altera ligeramente los nombres de los argumentos definidos en camelCase para que sean aceptados como entradas de Q#. Por ejemplo, si en lugar de n decide usar el nombre numQubits anterior, esta entrada se proporcionaría en la línea de comandos mediante --num-qubits 4 en lugar de -n 4.

El mensaje de error también proporciona otras opciones que se pueden utilizar, incluido cómo cambiar la máquina de destino.

Diferentes máquinas de destino

Como las salidas de nuestras operaciones hasta ahora han sido los resultados esperados de su acción sobre cúbits reales, está claro que la máquina de destino predeterminada de la línea de comandos es el simulador cuántico de estado completo, QuantumSimulator. Sin embargo, se puede indicar a los invocables que se ejecuten en una máquina de destino específica con la opción --simulator (o la abreviatura -s).

Por ejemplo, podríamos ejecutarlos en ResourcesEstimator:

dotnet run -n 4 -s ResourcesEstimator

La salida impresa sería:

Metric          Sum
CNOT            0
QubitClifford   4
R               0
Measure         4
T               0
Depth           0
Width           4
BorrowedWidth   0

Para más información sobre lo que indican estas métricas, consulte Estimador de recursos: métricas notificadas.

Resumen de la ejecución de la línea de comandos


Q# program from command line

Opciones de dotnet run que no son de Q#

Como ya se ha mencionado brevemente con la opción --project, el comando dotnet run también acepta opciones no relacionadas con los argumentos invocables de Q#. Si se proporcionan ambos tipos de opciones, se deben proporcionar primero las opciones específicas de dotnet, seguidas de un delimitador -- y, a continuación, las opciones específicas de Q#. Por ejemplo, si se especifica una ruta de acceso junto con un número de cúbits para la operación anterior, se ejecutaría mediante dotnet run --project <PATH> -- -n <n>.

Q# con programas host

Con el archivo Q# preparado, una alternativa a la llamada directa de una operación o función desde la línea de comandos es utilizar un programa host en otro lenguaje clásico. Específicamente, esto se puede hacer con Python o con un lenguaje de .NET como C# o F# (por motivos de brevedad, solo detallaremos aquí C#). Se requiere un poco más de configuración para habilitar la interoperabilidad, pero esos detalles se pueden encontrar en las guías de instalación.

En pocas palabras, la situación incluye ahora un archivo de programa host (por ejemplo, *.py o *.cs) en la misma ubicación que nuestro archivo de Q#. Ahora, es el programa host el que se ejecuta. Mientras se ejecuta, puede llamar a funciones y operaciones específicas de Q# del archivo de Q#. El núcleo de la interoperabilidad se basa en que el compilador Q# haga que el contenido del archivo de Q# sea accesible para que el programa host pueda llamarlo.

Una de las principales ventajas de utilizar un programa host es que los datos clásicos devueltos por el programa de Q# se pueden procesar posteriormente en el lenguaje host. Este control podría consistir en algún procesamiento avanzado de datos (por ejemplo, algo que no se pueda realizar internamente en Q#) y luego llamar a otras acciones de Q# en función de esos resultados, o algo tan simple como trazar los resultados de Q#.

Aquí se muestra el esquema general y, a continuación, se describen las implementaciones específicas para Python y C#. Encontrará un ejemplo que utiliza un programa host de F# en los ejemplos de interoperabilidad de .NET.


Q# program from a host program

Nota:

El atributo @EntryPoint() utilizado para las aplicaciones de Q# no puede utilizarse con los programas host. Se producirá un error si está presente en el archivo de Q# al que el host está llamando.

Para trabajar con diferentes programas host, no es necesario realizar cambios en un archivo Q# de *.qs. Las siguientes implementaciones del programa host funcionan todas con el mismo archivo de Q#:

namespace Superposition {
    open Microsoft.Quantum.Intrinsic;     // for H
    open Microsoft.Quantum.Measurement;   // for MResetZ
    open Microsoft.Quantum.Canon;         // for ApplyToEach
    open Microsoft.Quantum.Arrays;        // for ForEach

    operation MeasureSuperposition() : Result {
        use q = Qubit();     // allocates qubit for use (automatically in |0>)
        H(q);                // puts qubit in superposition of |0> and |1>
        return MResetZ(q);   // measures qubit, returns result (and resets it to |0> before deallocation)
    }

    operation MeasureSuperpositionArray(n : Int) : Result[] {
        use qubits = Qubit[n];
        ApplyToEach(H, qubits); 
        return ForEach(MResetZ, qubits);    
    }
}

Seleccione la pestaña correspondiente al lenguaje host que le interese.

Un programa host de Python se construye como sigue:

  1. Importamos el módulo qsharp, que registra el cargador de módulos para la interoperabilidad de Q#. Esta importación permite que los espacios de nombres de Q# aparezcan como módulos de Python desde los que podemos "importar" los invocables de Q#. Técnicamente, no son los propios invocables de Q# los que se importan, sino los stubs de Python que permiten invocarlos. Estos stubs se comportan como objetos de las clases de Python. Se utilizan métodos en estos objetos para especificar las máquinas de destino a las que se enviará la operación al ejecutar el programa.

  2. Importamos los invocables de Q#, que invocaremos directamente; en este caso, MeasureSuperposition y MeasureSuperpositionArray.

    import qsharp
    from Superposition import MeasureSuperposition, MeasureSuperpositionArray
    

    Con el módulo qsharp importado, también puede importar los invocables directamente desde los espacios de nombres de la biblioteca de Q#.

  3. Junto con el código de Python normal, ahora puede ejecutar esos invocables en máquinas de destino específicas y asignar sus valores devueltos a variables para su uso futuro:

    random_bit = MeasureSuperposition.simulate()
    print(random_bit)
    

Diagnóstico

Al igual que con los cuadernos de Q# independientes, también puede usar diagnósticos como DumpMachine y DumpOperation desde los cuadernos de Python para aprender cómo funciona el programa de Q# y para ayudar a diagnosticar problemas y errores en los programas de Q#.

namespace DumpOperation {
    open Microsoft.Quantum.Diagnostics;

    operation DumpPlusState() : Unit {
        use q = Qubit();
        within {
            H(q);
        } apply {
            DumpMachine();
        }
    }
}
from  DumpOperation import DumpPlusState
print(DumpPlusState.simulate())

Llamar a DumpMachine function genera la siguiente salida:

# wave function for qubits with ids (least to most significant): 0
∣0❭:     0.707107 +  0.000000 i  ==     ***********          [ 0.500000 ]     --- [  0.00000 rad ]
∣1❭:     0.707107 +  0.000000 i  ==     ***********          [ 0.500000 ]     --- [  0.00000 rad ]

El paquete Q# también permite capturar estos diagnósticos y manipularlos como objetos de Python:

with qsharp.capture_diagnostics() as diagnostics:
    DumpPlusState.simulate()
print(diagnostics)
[{'diagnostic_kind': 'state-vector',
  'div_id': 'dump-machine-div-7d3eac24-85c5-4080-b123-4a76cacaf58f',
  'qubit_ids': [0],
  'n_qubits': 1,
  'amplitudes': [{'Real': 0.7071067811865476,
    'Imaginary': 0.0,
    'Magnitude': 0.7071067811865476,
    'Phase': 0.0},
   {'Real': 0.7071067811865476,
    'Imaginary': 0.0,
    'Magnitude': 0.7071067811865476,
    'Phase': 0.0}]}]

Trabajar con JSON sin formato para diagnósticos puede resultar inconveniente, por lo que la función capture_diagnostics también admite la conversión de diagnósticos en objetos cuánticos mediante la biblioteca QuTiP:


with qsharp.capture_diagnostics(as_qobj=True) as diagnostics:
    DumpPlusState.simulate()
diagnostics[0]
Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[0.707]
 [0.707]]

Para más información sobre las características de diagnóstico que ofrecen Q# y el kit de desarrollo de Quantum, consulte Pruebas y depuración.

Especificación de las máquinas de destino

La ejecución de operaciones Q# en una máquina de destino específica se realiza por medio de la invocación de métodos de Python directamente en el objeto de operación importado. Por lo tanto, no es necesario crear un objeto para el destino de ejecución (por ejemplo, un simulador), sino que hay que invocar uno de los métodos siguientes para ejecutar la operación Q# importada:

Para obtener más información sobre máquinas de destino locales, vea Simuladores cuánticos.

Paso de argumentos a invocables en Q#

Los argumentos del invocable Q# deben proporcionarse en forma de argumento de palabra clave, donde la palabra clave es el nombre del argumento en la definición del invocable de Q#. Es decir, MeasureSuperpositionArray.simulate(n=4) es válido, mientras que MeasureSuperpositionArray.simulate(4) produciría un error.

Por lo tanto, el programa host de Python:

import qsharp
from Superposition import MeasureSuperposition, MeasureSuperpositionArray

single_qubit_result = MeasureSuperposition.simulate()
single_qubit_resources = MeasureSuperposition.estimate_resources()

multi_qubit_result = MeasureSuperpositionArray.simulate(n=4)
multi_qubit_resources = MeasureSuperpositionArray.estimate_resources(n=4)

print('Single qubit:\n' + str(single_qubit_result))
print(single_qubit_resources)

print('\nMultiple qubits:\n' + str(multi_qubit_result))
print(multi_qubit_resources)

da como resultado una salida como la siguiente:

Single qubit:
1
{'CNOT': 0, 'QubitClifford': 1, 'R': 0, 'Measure': 1, 'T': 0, 'Depth': 0, 'Width': 1, 'BorrowedWidth': 0}

Multiple qubits:
[0, 1, 1, 1]
{'CNOT': 0, 'QubitClifford': 4, 'R': 0, 'Measure': 4, 'T': 0, 'Depth': 0, 'Width': 4, 'BorrowedWidth': 0}

También es posible pasar matrices de forma similar. Puede ver un ejemplo en el ejemplo de síntesis lógica reversible.

No es posible pasar cúbits como argumentos desde código clásico. Cualquier lógica relacionada con tipos Q#, como Qubit, debe estar en el código de Q#. Si desea que el código de Python especifique el número de cúbits, podría tener algo parecido al parámetro nQubits : Int para la operación de Q#. El código de Python podría pasar el número de cúbits como un entero y, a continuación, el código de Q# podría asignar la matriz del número adecuado de cúbits.

Para los tipos Pauli y Result, hay definidas enumeraciones de Python, por lo que podría pasar esos valores directamente si lo desea. Consulte qsharp.Pauli y qsharp.Result.

Uso de código de Q# de otros proyectos o paquetes

De forma predeterminada, el comando import qsharp carga todos los archivos de .qs de la carpeta actual y hace que sus operaciones y funciones de Q# estén disponibles para su uso desde el interior del script de Python.

Para cargar el código de Q# desde otra carpeta, se puede usar qsharp.projects API para añadir una referencia a un archivo de .csproj para un proyecto de Q# (es decir, un proyecto que hace referencia a Microsoft.Quantum.Sdk). Este comando compilará cualquier archivo .qs en la carpeta que contiene .csproj y sus subcarpetas. También cargará recursivamente cualquier paquete al que se haga referencia mediante PackageReference o los proyectos de Q# a los que se haga referencia mediante ProjectReference en ese archivo de .csproj.

Como ejemplo, el siguiente código Python importa un proyecto externo, con su ruta relativa haciendo referencia a la carpeta actual, e invoca una de sus operaciones de Q#:

import qsharp
qsharp.projects.add("../qrng/Qrng.csproj")
from Qrng import SampleQuantumRandomNumberGenerator
print(f"Qrng result: {SampleQuantumRandomNumberGenerator.simulate()}")

Este código da como resultado la salida siguiente:

Adding reference to project: ../qrng/Qrng.csproj
Qrng result: 0

Para cargar paquetes externos que contengan código de Q#, utilice qsharp.packages API.

Si el código de Q# de la carpeta actual depende de proyectos o paquetes externos, es posible que aparezcan errores al ejecutar import qsharp, ya que las dependencias aún no se han cargado. Para cargar los paquetes externos necesarios o los proyectos de Q# durante el comando import qsharp, asegúrese de que la carpeta donde está el script de Python contenga un archivo .csproj que haga referencia a Microsoft.Quantum.Sdk. En .csproj, añada la propiedad <IQSharpLoadAutomatically>true</IQSharpLoadAutomatically> a <PropertyGroup>. Este cambio indicará a IQ# que cargue recursivamente cualquier elemento ProjectReference o PackageReference que se encuentre en ese archivo .csproj durante el comando import qsharp.

Por ejemplo, este es un archivo de .csproj sencillo que hace que IQ# cargue automáticamente el paquete de Microsoft.Quantum.Chemistry:

<Project Sdk="Microsoft.Quantum.Sdk/0.17.2105143879">
    <PropertyGroup>
        <OutputType>Library</OutputType>
        <TargetFramework>netstandard2.1</TargetFramework>
        <IQSharpLoadAutomatically>true</IQSharpLoadAutomatically>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.Quantum.Chemistry" Version="0.17.2105143879" />
    </ItemGroup>
</Project>

Nota:

Actualmente, los hosts de Python requieren esta propiedad <IQSharpLoadAutomatically> personalizada, pero en el futuro podría convertirse en el comportamiento predeterminado para los archivos de .csproj ubicados en la misma carpeta que el script de Python.

Nota

Actualmente, los hosts de Python omiten la opción de configuración <QsharpCompile> de .csproj, y todos los archivos de .qs de la carpeta de .csproj (incluidas las subcarpetas) se cargan y se compilan. La compatibilidad con las opciones de configuración de .csproj se mejorará en futuras versiones (consulte iqsharp#277 para más información).

Cuadernos de Jupyter Notebook en Q#

Los cuadernos en Q# de Jupyter Notebook utilizan el kernel IQ#, que permite definir, compilar y ejecutar invocables de Q# en un solo cuaderno, además de instrucciones, notas y otro contenido. Esto significa que, aunque es posible importar y utilizar el contenido de archivos *.qs de Q#, no son necesarios en el modelo de ejecución.

Aquí, se detalla cómo ejecutar las operaciones de Q# definidas anteriormente. No obstante, encontrará información más detallada sobre cómo usar cuadernos de Jupyter de Q# en Configuración de un entorno de Q# independiente.

Definición de las operaciones

En un cuaderno en Q# de Jupyter Notebook, el código de Q# se introduce igual que haríamos desde el espacio de nombres de un archivo de Q#.

Así, se puede habilitar el acceso a los invocables desde las bibliotecas estándar de Q# con instrucciones de open para sus respectivos espacios de nombres. Al ejecutar una celda con una instrucción de este tipo, las definiciones de esos espacios de nombres están disponibles en todo el área de trabajo.

Nota

Los invocables de Microsoft.Quantum.Intrinsic y Microsoft.Quantum.Canon (por ejemplo, H y ApplyToEach) están automáticamente disponibles para las operaciones definidas dentro de las celdas en cuadernos en Q# de Jupyter Notebook. Sin embargo, esto no se cumple para el código traído de archivos de código fuente externos de Q# (un proceso que se muestra en Introducción a Q# y los cuadernos de Jupyter Notebook).

Del mismo modo, para definir las operaciones, basta con escribir el código de Q# y ejecutar la celda.

Jupyter cell defining Q# operations

A continuación, la salida enumera esas operaciones, que pueden llamarse desde futuras celdas.

Máquinas de destino

La funcionalidad para ejecutar operaciones en máquinas de destino específicas se proporciona a través de comandos magic de IQ#. Por ejemplo, %simulate usa QuantumSimulator, y %estimate usa ResourcesEstimator:

Jupyter cell simulating a Q# operation and running resource estimation

Paso de argumentos a invocables en Q#

Para pasar datos de entrada a las operaciones de Q#, los argumentos pueden pasarse como pares key=value al comando magic de ejecución. Así, para ejecutar MeasureSuperpositionArray con cuatro cúbits, podemos ejecutar %simulate MeasureSuperpositionArray n=4:

Jupyter cell simulating a Q# operation with arguments

Este patrón se puede utilizar de forma similar con %estimate y otros comandos de ejecución.

Nota:

No es posible pasar invocables de una manera similar con comandos de ejecución como %simulate o %estimate.

Uso de código de Q# de otros proyectos o paquetes

De forma predeterminada, un cuaderno en Q# de Jupyter Notebook carga todos los archivos de .qs de la carpeta actual y hace que sus operaciones y funciones de Q# estén disponibles para su uso desde el interior del cuaderno. El comando magic %who enumera todas las operaciones y funciones de Q# disponibles actualmente.

Para cargar el código de Q# desde otra carpeta, se puede usar el comando magic de %project para añadir una referencia a un archivo de .csproj para un proyecto de Q# (es decir, un proyecto que hace referencia a Microsoft.Quantum.Sdk). Este comando compilará cualquier archivo .qs en la carpeta que contiene .csproj, y sus subcarpetas. También cargará recursivamente cualquier paquete al que se haga referencia mediante PackageReference o los proyectos de Q# a los que se haga referencia mediante ProjectReference en ese archivo de .csproj.

Como ejemplo, las siguientes celdas simulan una operación de Q# desde un proyecto externo, y se hace referencia a la ruta del proyecto en relación con la carpeta actual:

Jupyter cell simulating a Q# operation from an external project

Para cargar paquetes externos que contengan código de Q#, utilice el comando magic de %package. La carga de un paquete también hará que estén disponibles los comandos magic personalizados o codificadores de visualización incluidos en cualquier ensamblaje que forme parte del paquete.

Para cargar paquetes externos o proyectos de Q# en el momento de inicializar el cuaderno, asegúrese de que la carpeta del cuaderno contenga un archivo .csproj que haga referencia a Microsoft.Quantum.Sdk. En .csproj, añada la propiedad <IQSharpLoadAutomatically>true</IQSharpLoadAutomatically> a <PropertyGroup>. Esto indicará a IQ# que cargue recursivamente cualquier elemento ProjectReference o PackageReference que se encuentre en ese archivo .csproj en el momento de la carga del cuaderno.

Por ejemplo, este es un archivo de .csproj sencillo que hace que IQ# cargue automáticamente el paquete de Microsoft.Quantum.Chemistry:

<Project Sdk="Microsoft.Quantum.Sdk/0.17.2105143879">
    <PropertyGroup>
        <OutputType>Library</OutputType>
        <TargetFramework>netstandard2.1</TargetFramework>
        <IQSharpLoadAutomatically>true</IQSharpLoadAutomatically>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.Quantum.Chemistry" Version="0.17.2105143879" />
    </ItemGroup>
</Project>

Nota

Actualmente los hosts de Q# de Jupyter Notebook requieren esta propiedad <IQSharpLoadAutomatically> personalizada, pero en el futuro podría convertirse en el comportamiento predeterminado para un archivo de .csproj situado en la misma carpeta que el archivo del cuaderno.

Nota

Actualmente, los hosts en Q# de Jupyter Notebook omiten la opción de configuración <QsharpCompile> de .csproj, y todos los archivos de .qs de la carpeta de .csproj (incluidas las subcarpetas) se cargan y se compilan. La compatibilidad con las opciones de configuración de .csproj se mejorará en futuras versiones (consulte iqsharp#277 para más información).