Compartir a través de


Operaciones y funciones

Como se explica con más detalle en la descripción del tipo de datos de cúbit, los cálculos cuánticos se ejecutan en forma de efectos secundarios de las operaciones que se admiten de forma nativa en el procesador cuántico de destino. Estos son, de hecho, los únicos efectos secundarios en Q#. Dado que todos los tipos se inmutables, no hay efectos secundarios que afecten a un valor que se representa explícitamente en Q#. Por lo tanto, siempre que una implementación de un determinado invocable no llame directa o indirectamente a ninguna de estas operaciones implementadas de forma nativa, su ejecución siempre genera la misma salida, dada la misma entrada.

Q# permite dividir explícitamente estos cálculos puramente deterministas en funciones . Dado que el conjunto de instrucciones admitidas de forma nativa no es fijo ni está integrado en el propio lenguaje, sino totalmente configurable y expresado como una biblioteca de Q#, se garantiza el determinismo al exigir que las funciones solo puedan llamar a otras funciones y no puedan llamar a ninguna operación. Además, las instrucciones nativas que no son deterministas, es decir, porque afectan al estado cuántico, se representan como operaciones. Con estas dos restricciones, las funciones se pueden evaluar en cuanto se conoce su valor de entrada y, en principio, nunca es necesario evaluar más de una vez para la misma entrada.

Q#, por lo tanto, distingue entre dos tipos de invocables: operaciones y funciones. Todos los invocables toman un único argumento (potencialmente con valores de tupla) como entrada y generan un único valor (tupla) como salida. Sintácticamente, el tipo de operación se expresa como <TIn> => <TOut> is <Char>, donde <TIn> se va a reemplazar por el tipo de argumento, <TOut> se reemplazará por el tipo de valor devuelto y <Char> se reemplazará por las características de operación de . Si no es necesario especificar ninguna característica, la sintaxis simplifica <TIn> => <TOut>. Del mismo modo, los tipos de función se expresan como <TIn> -> <TOut>.

Aparte de esta garantía de determinismo, hay poca diferencia entre las operaciones y las funciones. Ambos son valores de primera clase que se pueden pasar libremente; se pueden usar como valores devueltos o argumentos para otros invocables, como se muestra en el ejemplo siguiente:

function Pow<'T>(op : 'T => Unit, pow : Int) : 'T => Unit {
    return PowImpl(op, pow, _);
}

Ambos se pueden crear instancias en función de una definición parametrizada de tipo, por ejemplo, la función de parametrizada función Pow anterior y se pueden aplicar parcialmente como se hace en la instrucción return del ejemplo.

Características de la operación

Además de la información sobre el tipo de entrada y salida, el tipo de operación contiene información sobre las características de una operación. Esta información, por ejemplo, describe qué functores son compatibles con la operación. Además, la representación interna también contiene información relevante para la optimización que el compilador deduce.

Las características de una operación son un conjunto de etiquetas predefinidas e integradas. Se expresan en forma de una expresión especial que forma parte de la firma de tipo. La expresión consta de uno de los conjuntos predefinidos de etiquetas o de una combinación de expresiones de características a través de un operador binario admitido.

Hay dos conjuntos predefinidos, Adj y Ctl.

  • Adj es el conjunto que contiene una sola etiqueta que indica que una operación es adyacente, lo que significa que admite el functor Adjoint y la transformación cuántica aplicada puede "deshacerse", es decir, se puede invertir.
  • Ctl es el conjunto que contiene una sola etiqueta que indica que una operación es controlable, lo que significa que admite la Controlled functor y su ejecución puede estar condicionada en el estado de otros cúbits.

Los dos operadores que se admiten como parte de las expresiones de características son la unión establecida + y la intersección del conjunto *. En EBNF (formulario Backus–Naur extendido),

    predefined = "Adj" | "Ctl";
    characteristics = predefined 
        | "(", characteristics, ")" 
        | characteristics ("+"|"*") characteristics;

Como cabría esperar, * tiene mayor prioridad que + y ambos son asociativos a la izquierda. El tipo de una operación unitaria, por ejemplo, se expresa como <TIn> => <TOut> is Adj + Ctl, donde <TIn> debe reemplazarse por el tipo del argumento operation y <TOut> reemplazado por el tipo del valor devuelto.

Nota:

La indicación de las características de una operación en este formato tiene dos principales ventajas; para una, se pueden introducir nuevas etiquetas sin tener exponencialmente muchas palabras clave de lenguaje para todas las combinaciones de etiquetas. Más importante, el uso de expresiones para indicar las características de una operación también admite parametrizaciones sobre las características de operación en el futuro.