Zarządzanie pamięcią kwantową
Program zawsze uruchamia się bez kubitów, co oznacza, że nie można przekazać wartości typu Qubit
jako argumentów punktu wejścia. To ograniczenie jest zamierzone, ponieważ celem Q# programu jest wyrażenie i uzasadnienie w całości.
Zamiast tego program przydziela i zwalnia kubity lub pamięć kwantową w miarę jego działania.
W tym względzie Q# modeluje komputer kwantowy jako stertę kubitu.
Zamiast obsługiwać oddzielne instrukcje przydzielania i wydawania dla pamięci kwantowej, Q# obsługuje alokację pamięci kwantowej w postaci instrukcji blokowych, gdzie pamięć jest dostępna tylko w zakresie tej instrukcji bloku. Blok instrukcji można niejawnie zdefiniować podczas przydzielania kubitów na czas trwania bieżącego zakresu, zgodnie z opisem bardziej szczegółowo w sekcjach dotyczących use
instrukcji i borrow
. Próba uzyskania dostępu do przydzielonych kubitów po zakończeniu instrukcji powoduje wyjątek środowiska uruchomieniowego.
Q# ma dwie instrukcje i use
borrow
, które tworzy wystąpienie wartości kubitów, tablice kubitów lub dowolną kombinację. Tych instrukcji można używać tylko w ramach operacji. Zbierają utworzone wartości kubitów, wiążą je ze zmiennymi określonymi w instrukcji, a następnie uruchamiają blok instrukcji.
Na końcu bloku powiązane zmienne wykraczają poza zakres i nie są już zdefiniowane.
Q# rozróżnia alokację czystych i zanieczyszczonych kubitów. Czyste kubity są niezaangangledowane i nie są używane przez inną część obliczeń. Zanieczyszczone kubity to kubity, których stan jest nieznany, a nawet może być splątane z innymi częściami pamięci procesora kwantowego.
Use, instrukcja
Czyste kubity są przydzielane przez instrukcję use
.
- Instrukcja składa się ze słowa kluczowego
use
, po którym następuje powiązanie i opcjonalny blok instrukcji. - Jeśli blok instrukcji jest obecny, kubity są dostępne tylko w tym bloku. W przeciwnym razie kubity są dostępne do końca bieżącego zakresu.
- Powiązanie jest zgodne z tym samym wzorcem co
let
instrukcje: pojedynczy symbol lub krotka symboli, a następnie znak=
równości , oraz pojedyncza krotka lub pasująca krotka inicjatorów.
Inicjatory są dostępne dla pojedynczego kubitu wskazanego jako Qubit()
, lub tablicy kubitów, Qubit[n]
gdzie n
jest wyrażeniem Int
.
Na przykład
use qubit = Qubit();
// ...
use (aux, register) = (Qubit(), Qubit[5]);
// ...
use qubit = Qubit() {
// ...
}
use (aux, register) = (Qubit(), Qubit[5]) {
// ...
}
Po alokacji kubity muszą mieć stan |0⟩. Są one zwalniane na końcu zakresu i muszą być w stanie |0⟩ po wydaniu. To wymaganie nie jest wymuszane przez kompilator, ponieważ wymagałoby to symbolicznej oceny, która szybko staje się zbyt kosztowna. W przypadku uruchamiania w symulatorach można wymusić wymaganie w czasie wykonywania. W przypadku procesorów kwantowych nie można wymusić wymagania w czasie wykonywania; Kubit niezmierzony może zostać zresetowany do |0⟩ za pomocą transformacji unitarnej. Nie można tego zrobić powoduje nieprawidłowe zachowanie.
Instrukcja use
przydziela kubity z wolnego sterty kubitu procesora kwantowego i zwraca je do sterty nie później niż koniec zakresu, w którym są powiązane kubity.
Pożyczanie instrukcji
Instrukcja borrow
udziela dostępu do kubitów, które są już przydzielone, ale nie są obecnie używane. Te kubity mogą być w dowolnym stanie i muszą być w tym samym stanie ponownie po zakończeniu oświadczenia pożyczki.
Niektóre algorytmy kwantowe mogą używać kubitów bez polegania na ich dokładnym stanie i nie wymagając, aby były niezaangangowane w pozostałej części systemu. Oznacza to, że wymagają one tymczasowo dodatkowych kubitów, ale mogą zapewnić, że te kubity są zwracane dokładnie do ich pierwotnego stanu, niezależnie od tego, który stan był.
Jeśli istnieją kubity, które są używane, ale nie dotykane podczas części podprokucji, te kubity można pożyczyć do użycia przez taki algorytm zamiast przydzielać dodatkową pamięć kwantową. Pożyczki zamiast przydzielania mogą znacząco zmniejszyć ogólne wymagania dotyczące pamięci kwantowej algorytmu i jest kwantowym przykładem typowego kompromisu w czasie kosmicznym.
Instrukcja borrow
jest zgodna z tym samym wzorcem opisanym wcześniej dla instrukcjiuse
z dostępnymi tymi samymi inicjatorami.
Na przykład
borrow qubit = Qubit();
// ...
borrow (aux, register) = (Qubit(), Qubit[5]);
// ...
borrow qubit = Qubit() {
// ...
}
borrow (aux, register) = (Qubit(), Qubit[5]) {
// ...
}
Pożyczone kubity są w nieznanym stanie i wychodzą z zakresu na końcu bloku instrukcji. Kredytobiorca zobowiązuje się do opuszczenia kubitów w tym samym stanie, co kiedy zostały one pożyczone; oznacza to, że ich stan na początku i koniec bloku instrukcji powinien być taki sam.
Instrukcja borrow
pobiera kubity w użyciu, które mają gwarancję, że nie będą używane przez program od momentu, gdy kubit jest powiązany do ostatniego użycia tego kubitu.
Jeśli nie ma wystarczającej liczby kubitów dostępnych do wypożyczania, kubity są przydzielane z i zwracane do sterty jak use
instrukcja.
Uwaga
Wśród znanych przypadków użycia brudnych kubitów są implementacje wielokontrolerowych bram CNOT, które wymagają bardzo niewielu kubitów i implementacji inkrementatorów. Ten dokument dotyczący faktorowania za pomocą kubitów zawiera przykład algorytmu wykorzystującego pożyczone kubity.