Administración dinámica cuántica

Un programa siempre se inicia sin cúbits, lo que significa que no se pueden pasar valores de tipo Qubit como argumentos de punto de entrada. Esta restricción es intencional, ya que el propósito de Q# es expresar y razonar sobre un programa en su totalidad. En cambio, un programa asigna y libera cúbits, o memoria cuántica, a medida que avanza. De este modo, Q# modela el ordenador cuántico como un montón de cúbits.

En lugar de admitir instrucciones de asignación y liberación separadas para la memoria cuántica, Q# admite la asignación de memoria cuántica en forma de instrucciones de bloque, donde la memoria es accesible solo dentro del ámbito de esa instrucción de bloque. El bloque de instrucciones se puede definir implícitamente al asignar cúbits para la duración del ámbito actual, como se describe con más detalle en las secciones sobre las instrucciones use y borrow. Al intentar acceder a los cúbits asignados después de que finalice la instrucción, se producirá una excepción en tiempo de ejecución.

Q# tiene dos instrucciones, use y borrow, para crear una instancia de valores de cúbit, matrices de cúbits, o cualquier combinación de ellos. Solo puede usar estas instrucciones dentro de las operaciones. Obtienen las instancias creadas de los valores de cúbit, los vinculan a las variables especificadas en la instrucción y, a continuación, ejecutan un bloque de instrucciones. Al final del bloque, las variables enlazadas se sale del ámbito y ya no se definen.

Q# distingue entre la asignación de cúbits limpios y sucios. Los cúbits limpios no están entrelazados y no se usan en otra parte del cálculo. Los cúbits sucios son cúbits cuyo estado es desconocido e incluso pueden estar entrelazados con otras partes de la memoria del procesador cuántico.

Instrucción use

Los cúbits limpios se asignan mediante la instrucción use.

  • La instrucción consiste en la palabra clave use seguida de un enlace y un bloque de instrucciones opcional.
  • Si hay un bloque de instrucciones, los cúbits solo están disponibles dentro de ese bloque. De lo contrario, los bits cúbits estarán disponibles hasta el final del ámbito actual.
  • El enlace sigue el mismo patrón que las sentencias let: un único símbolo o una tupla de símbolos, seguido de un signo igual =, y una tupla única o una tupla de inicializadores que coincidan.

Los inicializadores están disponibles para un solo cúbit, que se indica como Qubit(), o una matriz de cúbits, Qubit[n], donde n es una expresión Int. Por ejemplo,

use qubit = Qubit();
// ...

use (aux, register) = (Qubit(), Qubit[5]);
// ...

use qubit = Qubit() {
    // ...
}

use (aux, register) = (Qubit(), Qubit[5]) {
    // ...
}

Se garantiza que los cúbits están en un estado de |0⟩ en el momento de la asignación Se liberan al final del ámbito y deben estar en un estado |0⟩ al liberarse. Este requisito no viene aplicado por el compilador, ya que esto requeriría una evaluación simbólica que se vuelve prohibitivamente costosa rápidamente. Cuando se ejecuta en simuladores, el requisito se puede aplicar en tiempo de ejecución. En los procesadores cuánticos, el requisito no puede aplicarse en tiempo de ejecución; un cúbit no medido puede restablecerse a |0⟩ mediante una transformación unitaria. De no ser así, se producirá un comportamiento incorrecto.

La instrucción use asigna los cúbits del montón de cúbits libres del procesador cuántico y los devuelve al montón antes del final del ámbito en el que están enlazados los cúbits.

Instrucción borrow

La instrucción borrow concede acceso a cúbits que ya están asignados, pero que no están actualmente en uso. Estos cúbits pueden estar en un estado arbitrario y deben estar de nuevo en el mismo estado cuando finalice la instrucción borrow. Algunos algoritmos cuánticos pueden usar cúbits sin depender de su estado exacto y sin necesidad de que no estén entrelazados con el resto del sistema. Es decir, requieren temporalmente cúbits adicionales, pero pueden garantizar que esos cúbits vuelvan exactamente a su estado original, independientemente del estado en que se encontrasen.

Si hay cúbits que están en uso pero que no se tocan durante partes de una subrutina, esos cúbits pueden tomarse prestados para que los utilice un algoritmo de este tipo en lugar de asignar memoria cuántica adicional. Tomar prestado en lugar de asignar puede reducir significativamente los requisitos generales de memoria cuántica de un algoritmo, y es un ejemplo cuántico de una típica compensación espacio-temporal.

Una instrucción borrow sigue el mismo patrón descrito anteriormente para la instrucción use, con los mismos inicializadores disponibles. Por ejemplo,

borrow qubit = Qubit();
// ...

borrow (aux, register) = (Qubit(), Qubit[5]);
// ...

borrow qubit = Qubit() {
    // ...
}

borrow (aux, register) = (Qubit(), Qubit[5]) {
    // ...
}

Los cúbits prestados están en un estado desconocido y salen del ámbito al final del bloque de instrucciones. El prestatario se compromete a dejar los cúbits en el mismo estado en el que estaban cuando los pidió prestados, es decir, se espera que su estado al principio y al final del bloque de instrucciones sea el mismo.

La instrucción borrow recupera los cúbits en uso que se garantiza que el programa no va a utilizar desde el momento en que se enlaza el cúbit hasta el último uso de ese cúbit. Si no hay suficientes cúbits disponibles para tomarlos prestados, los cúbits se asignarán desde el montón y se devolverán a él como en una instrucción use.

Nota

Entre los casos de uso conocidos de cúbits sucios se encuentran las implementaciones de puertas CNOT multicontroladas que requieren muy pocos cúbits y las implementaciones de incrementadores. En este documento sobre la factorización con cúbits, se proporciona un ejemplo de un algoritmo que utiliza cúbits prestados.