Operacje i funkcje
Jak opisano bardziej szczegółowo w opisie typu danych kubitów, obliczenia kwantowe są wykonywane w postaci skutków ubocznych operacji, które są natywnie obsługiwane na docelowym procesorze kwantowym. Są to w rzeczywistości jedyne skutki uboczne w Q#. Ponieważ wszystkie typy są niezmienne, nie ma żadnych skutków ubocznych, które wpływają na wartość, która jest jawnie reprezentowana w Q#. W związku z tym, o ile implementacja określonego wywołania nie wywołuje bezpośrednio ani pośrednio żadnej z tych natywnie zaimplementowanych operacji, jego wykonanie zawsze generuje te same dane wyjściowe, biorąc pod uwagę te same dane wejściowe.
Q# umożliwia jawne podzielenie takich czysto deterministycznych obliczeń na funkcje . Ponieważ zestaw natywnie obsługiwanych instrukcji nie jest stały i wbudowany w sam język, ale raczej w pełni konfigurowalny i wyrażony jako biblioteka Q#, determinizm jest gwarantowany przez wymaganie, aby funkcje mogły wywoływać tylko inne funkcje i nie mogą wywoływać żadnych operacji. Ponadto natywne instrukcje, które nie są deterministyczne, to znaczy, że mają wpływ na stan kwantowy, są reprezentowane jako operacje. Dzięki tym dwóm ograniczeniom funkcje można oceniać tak szybko, jak tylko ich wartość wejściowa jest znana, a w zasadzie nigdy nie trzeba oceniać więcej niż raz dla tych samych danych wejściowych.
Q# dlatego rozróżnia dwa typy wywoływanych: operacje i funkcje. Wszystkie wywołania przyjmują jeden argument (potencjalnie wartość krotki) jako dane wejściowe i tworzą pojedynczą wartość (krotkę) jako dane wyjściowe. Syntaktycznie typ operacji jest wyrażany jako <TIn> => <TOut> is <Char>
, gdzie <TIn>
ma zostać zastąpiony przez typ argumentu, <TOut>
ma zostać zastąpiony przez typ zwracany, a <Char>
ma zostać zastąpiona przez właściwości operacji . Jeśli nie trzeba określać żadnych cech, składnia upraszcza <TIn> => <TOut>
. Podobnie typy funkcji są wyrażane jako <TIn> -> <TOut>
.
Oprócz tej gwarancji determinizmu istnieje niewielka różnica między operacjami i funkcjami. Oba są wartościami pierwszej klasy, które można swobodnie przekazywać; mogą być używane jako wartości zwracane lub argumenty do innych elementów wywołujących, jak pokazano w poniższym przykładzie:
function Pow<'T>(op : 'T => Unit, pow : Int) : 'T => Unit {
return PowImpl(op, pow, _);
}
Oba te elementy można utworzyć na podstawie definicji parametryzowanej typu, na przykład parametryzowanego typu funkcji Pow
wcześniejszej i można je częściowo zastosowanych zgodnie z instrukcją return
w przykładzie.
Charakterystyka operacji
Oprócz informacji o typie danych wejściowych i wyjściowych typ operacji zawiera informacje o cechach operacji. Te informacje zawierają na przykład opis elementów funktorowych obsługiwanych przez operację. Ponadto wewnętrzna reprezentacja zawiera również informacje istotne dla optymalizacji, które są wnioskowane przez kompilator.
Cechy operacji to zestaw wstępnie zdefiniowanych i wbudowanych etykiet. Są one wyrażane w postaci wyrażenia specjalnego, które jest częścią podpisu typu. Wyrażenie składa się z jednego ze wstępnie zdefiniowanych zestawów etykiet lub kombinacji wyrażeń cech za pośrednictwem obsługiwanego operatora binarnego.
Istnieją dwa wstępnie zdefiniowane zestawy, Adj
i Ctl
.
-
Adj
jest zestawem zawierającym pojedynczą etykietę wskazującą, że operacja jest przylegana, co oznacza, że obsługujeAdjoint
functor, a zastosowana transformacja kwantowa może być "cofniętą", czyli może zostać odwrócona. -
Ctl
jest zestawem zawierającym pojedynczą etykietę wskazującą, że operacja jest sterowana, co oznacza, że obsługujeControlled
functor, a jego wykonanie może być warunkowe w stanie innych kubitów.
Dwa operatory obsługiwane jako część wyrażeń charakterystyki to zestaw +
i zestaw przecięcie *
.
W systemie EBNF (formularz rozszerzony Backus–Naur),
predefined = "Adj" | "Ctl";
characteristics = predefined
| "(", characteristics, ")"
| characteristics ("+"|"*") characteristics;
Jak można by się spodziewać, *
ma wyższy pierwszeństwo niż +
, a oba są lewe asocjacyjne. Typ operacji jednostkowej, na przykład, jest wyrażony jako <TIn> => <TOut> is Adj + Ctl
, gdzie <TIn>
należy zastąpić typem argumentu operacji, a <TOut>
zastąpiony typem zwracanej wartości.
Uwaga
Wskazanie cech operacji w tej formie ma dwie główne zalety; dla jednego można wprowadzać nowe etykiety bez wykładniczo wielu słów kluczowych języka dla wszystkich kombinacji etykiet. Co ważniejsze, używanie wyrażeń w celu wskazania cech operacji obsługuje również parametryzacje cech operacji w przyszłości.