Operacje i funkcje

Jak bardziej szczegółowo opisano w opisie typu danych kubitu, 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 skutków ubocznych, które wpływają na wartość, która jest jawnie reprezentowana w Q#programie . 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 instrukcji obsługiwanych natywnie nie jest stały i wbudowany w sam język, ale raczej w pełni konfigurowalny i wyrażony jako Q# biblioteka, determinizm jest gwarantowany przez wymaganie, aby funkcje mogły wywoływać tylko inne funkcje i nie mogą wywoływać żadnych operacji. Ponadto instrukcje natywne, które nie są deterministyczne, czyli ponieważ wpływają na stan kwantowy, są reprezentowane jako operacje. W przypadku tych dwóch ograniczeń funkcje można ocenić tak szybko, jak ich wartość wejściowa jest znana, a w zasadzie nigdy nie trzeba oceniać więcej niż raz dla tych samych danych wejściowych.

Q# w związku z tym rozróżnia dwa typy elementów wywołujących: operacje i funkcje. Wszystkie wywołania przyjmują jeden argument (potencjalnie wartości krotki) jako dane wejściowe i tworzą pojedynczą wartość (krotkę) jako dane wyjściowe. Syntaktycznie typ operacji jest wyrażony jako <TIn> => <TOut> is <Char>, gdzie <TIn> ma zostać zastąpiony przez typ argumentu, <TOut> ma zostać zastąpiony przez typ zwracany i <Char> ma zostać zastąpiony przez cechy operacji. Jeśli nie trzeba określać żadnych cech, składnia upraszcza proces <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 wystąpienia można utworzyć na podstawie definicji parametryzowanej typu, na przykład parametryzowanej funkcji Pow typu powyżej i można je częściowo zastosować 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, na przykład, opisują, jakie funkcje functors są obsługiwane 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żone 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 pomocą obsługiwanego operatora binarnego.

Istnieją dwa wstępnie zdefiniowane zestawy Adj i Ctl.

  • Adj to zestaw zawierający pojedynczą etykietę wskazującą, że operacja jest adjointable, co oznacza, że obsługuje Adjoint functor , a zastosowana transformacja kwantowa może być "cofniętą", czyli może zostać odwrócona.
  • Ctl to zestaw zawierający pojedynczą etykietę wskazującą, że operacja jest sterowana, co oznacza, że obsługuje Controlled functor , a jego wykonanie może być warunkowe w stanie innych kubitów.

Dwa operatory, które są obsługiwane jako część wyrażeń charakterystyki, to zestaw unii + i zestawu przecięcia *. W systemie EBNF,

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

Jak można się spodziewać, * ma wyższy pierwszeństwo niż + i oba są lewo-asocjacyjne. Typ operacji unitarnej, na przykład, jest wyrażony jako <TIn> => <TOut> is Adj + Ctl, gdzie <TIn> należy zastąpić typem argumentu operacji i <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. Być może ważniejsze jest użycie wyrażeń w celu wskazania cech operacji również obsługuje parametryzacje dotyczące cech operacji w przyszłości.