Eğitici Kılavuz: İçinde Kuantum Fourier Dönüşümünü Uygulama Q#

Bu öğreticide, tek tek kubitler üzerinde çalışan temel bir kuantum programının nasıl yazılıp simülasyonu yapılır gösterilmektedir.

Öncelikle büyük ölçekli kuantum programları için üst düzey bir programlama dili olarak oluşturulmuş olsa Q# da, kuantum programlamanın alt düzeyini, yani belirli kubitleri doğrudan ele almak için de kullanılabilir. Özellikle, bu öğretici birçok büyük kuantum algoritmasının ayrılmaz bir alt parçası olan Quantum Fourier Dönüşümü'ne (QFT) daha yakından bakar.

Bu öğreticide şunların nasıl yapılacağını öğreneceksiniz:

  • içinde Q#kuantum işlemlerini tanımlayın.
  • Quantum Fourier Dönüşüm devresini yazın.
  • Kubit ayırma işleminden ölçüm çıktısına kadar kuantum işlemini simüle et.
  • Kuantum sisteminin simülasyon dalga işlevinin işlem boyunca nasıl geliştiğini gözlemleyin.

Not

Kuantum bilgi işlemenin bu alt düzey görünümü genellikle bir sistemin belirli kubitlerine geçitlerin veya işlemlerin sıralı uygulamasını temsil eden kuantum devreleri açısından açıklanmaktadır. Bu nedenle, sırayla uyguladığınız tek ve çok kubitli işlemler devre diyagramlarında kolayca temsil edilebilir. Örneğin, bu öğreticide kullanılan üç kubitli kuantum Fourier dönüşümlerinin tamamı, devre olarak aşağıdaki gösterime sahiptir: Quantum Fourier Dönüşüm devresinin diyagramı.

Önkoşullar

Yeni Q# dosya oluşturma

  1. VS Code'da Dosya menüsünü açın ve Yeni Metin Dosyası'nı seçin.
  2. Dosyayı QFTcircuit.qs olarak kaydedin. Bu dosya, programınızın Q# kodunu içerir.
  3. QFTcircuit.qs dosyasını açın.

Q# içinde bir QFT devresi yaz.

Bu öğreticinin ilk bölümü, üç kubit üzerinde kuantum Fourier dönüşümünü gerçekleştiren işlemini Q#tanımlamaktırMain. DumpMachine işlevi, üç kubitli sistemin simülasyon dalga işlevinin işlem genelinde nasıl geliştiğini gözlemlemek için kullanılır. Öğreticinin ikinci bölümünde ölçüm işlevselliği ekler ve kubitlerin ölçüm öncesi ve son durumlarını karşılaştırırsınız.

İşlemi adım adım oluşturursunuz. Aşağıdaki bölümlerde yer alan kodu kopyalayıp QFTcircuit.qs dosyasına yapıştırın.

Bu bölümün tam Q# kodunu başvuru olarak görüntüleyebilirsiniz.

Gerekli Q# kitaplıkları içeri aktarın

Dosyanızın Q# içinde ilgili Std.* ad alanlarını içeri aktarın.

import Std.Diagnostics.*;
import Std.Math.*;
import Std.Arrays.*;

// operations go here

Bağımsız değişkenler ve dönüş değerleri ile işlemleri tanımla

Ardından işlemi tanımlayın Main :

operation Main() : Unit {
    // do stuff
}

İşlem Main asla bağımsız değişken almaz ve şu anda C#'ta Unit döndürüldüğünde ya da Python'da boş bir tanımlama grubu, void, döndürüldüğünde olduğuna benzer bir Tuple[()] nesnesi döndürür. Daha sonra, bir ölçüm sonuçları dizisi döndürmek için işlemi değiştirirsiniz.

Kubitleri ayır

İşlemin içinde Q# anahtar sözcüğüyle use üç kubitin bir kaydını ayırın. ile use, kubitler otomatik olarak $\ket{0}$ durumunda ayrılır.

use qs = Qubit[3]; // allocate three qubits

Message("Initial state |000>:");
DumpMachine();

Gerçek kuantum hesaplamalarında olduğu gibi Q# kubit durumlarına doğrudan erişmenize izin vermez. Ancak, DumpMachine işlemi target makinenin geçerli durumunu yazdırır, böylece tam durum simülatörüyle birlikte kullanıldığında hata ayıklama ve öğrenme için değerli içgörüler sağlayabilir.

Tek kubitli ve denetimli işlemleri uygulama

Ardından, işlemin kendisini oluşturan Main işlemleri uygulayın. Q# zaten ad alanında Std.Intrinsic bunların çoğunu ve diğer temel kuantum işlemlerini içerir.

Not

Std.Intrinsic diğer ad alanlarıyla birlikte önceki kod parçacığına import edilmedi, çünkü tüm Q# programları için derleyici tarafından otomatik olarak yüklenir.

İlk qubite uygulanan ilk işlem H (Hadamard) işlemidir.

Üç kubit QFT için ilk Hadamard'dan geçen bir devreyi gösteren diyagram.

Bir yazmaçtan belirli bir kubite bir işlem uygulamak için (örneğin, bir dizi Qubit içindeki tek Qubit[]), standart dizin gösterimini kullanın. Bu nedenle, kaydın H ilk kubitine qs işlemi uygulamak şu şekilde olur:

H(qs[0]);

QFT devresi, işlemi tek tek kubitlere uygulamanın H yanı sıra öncelikli olarak kontrollü R1 döndürmelerden oluşur. Genel olarak bir R1(θ, <qubit>) işlem, kubitin $\ket{0}$ bileşenini değiştirmeden bırakırken $\ket{1}$ bileşenine $e^{i\theta}$ döndürme uygular.

Q# bir işlemin çalışmasını bir veya birden çok denetim kubitine göre koşula bağlamayı kolaylaştırır. Genel olarak, çağrı Controlled ile başlar ve işlev bağımsız değişkenleri aşağıdaki gibi değişir:

Op(<normal args>) $\to$ Controlled Op([<control qubits>], (<normal args>))

Denetim kubiti bağımsız değişkeninin, tek bir kubit için olsa bile, bir dizi olması gerektiği unutulmamalıdır.

QFT'deki denetlenen işlemler, ilk kubit üzerinde işlem gerçekleştiren (ve ikinci ve üçüncü kubitler tarafından denetlenen) işlemlerdir R1 :

İlk kubit üzerinden üç-kubitli Quantum Fourier Dönüşümü devresini gösteren diyagram.

Dosyanızda Q# bu işlemleri şu ifadelerle çağırın:

Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));

PI() işlevi, döndürmeleri pi radyan açısından tanımlamak için kullanılır.

SWAP işlemi uygulama

İkinci ve üçüncü kubitlere ilgili H işlemleri ve kontrollü döndürmeleri uyguladıktan sonra bağlantı hattı şöyle görünür:

//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));

//third qubit:
H(qs[2]);

Son olarak, devreyi tamamlamak için birinci ve üçüncü kubitlere bir SWAP işlem uygularsınız. Kuantum Fourier dönüşümü kubitleri ters sırada çıkardığından bu işlem gereklidir, bu nedenle değiştirme işlemleri alt düğümün daha büyük algoritmalarla sorunsuz tümleştirilmesine olanak tanır.

SWAP(qs[2], qs[0]);

İşlem Q# artık kuantum Fourier dönüşümünün kubit düzeyindeki işlemlerini içerir:

Üç kubit Quantum Fourier Dönüşümü için bir bağlantı hattını gösteren diyagram.

Kubitleri serbest bırakma

Son adım, işlem sonrası durumunu görmek ve kubitleri serbest bırakmak için yeniden çağırmaktır DumpMachine() . Kubitleri tahsis ettiğinizde $\ket{0}$ durumundaydı ve ResetAll işlemi kullanılarak ilk durumlarına sıfırlanmaları gerekiyor.

Tüm kubitlerin açıkça $\ket{0}$ olarak sıfırlanması zorunluluğu, diğer işlemlerin Q#aynı kubitleri (kıt kaynak) kullanmaya başladığında durumlarını tam olarak bilmesini sağladığından temel bir özelliğidir. Ayrıca bunları sıfırlamak, sistemdeki diğer kübitlerle dolanık olmamalarını garanti eder. Sıfırlama bir use ayırma bloğunun sonunda gerçekleştirilmezse bir çalışma zamanı hatası ortaya çıkabilir.

Dosyanıza Q# aşağıdaki satırları ekleyin:

Message("After:");
DumpMachine();

ResetAll(qs); // deallocate qubits

Tam QFT işlemi

Program Q# tamamlandı. QFTcircuit.qs dosyanız şu şekilde görünmelidir:

import Std.Diagnostics.*;
import Std.Math.*;
import Std.Arrays.*;

operation Main() : Unit {

    use qs = Qubit[3]; // allocate three qubits

    Message("Initial state |000>:");
    DumpMachine();

    //QFT:
    //first qubit:
    H(qs[0]);
    Controlled R1([qs[1]], (PI()/2.0, qs[0]));
    Controlled R1([qs[2]], (PI()/4.0, qs[0]));

    //second qubit:
    H(qs[1]);
    Controlled R1([qs[2]], (PI()/2.0, qs[1]));

    //third qubit:
    H(qs[2]);

    SWAP(qs[2], qs[0]);

    Message("After:");
    DumpMachine();

    ResetAll(qs); // deallocate qubits

}                                                                                                                                                                               

QFT devresini çalıştırma

Şimdilik işlem Main herhangi bir değer döndürmez, işlem değer döndürür Unit . Daha sonra, işlemi bir ölçüm sonuçları dizisi (Result[]) döndürecek şekilde değiştirirsiniz.

  1. Programınızı çalıştırmak için, önce Q#gelen menüden seçin veya Main tuşlarına basın. Program, Main işlemi varsayılan simülatörde çalıştırır.
  2. Message ve DumpMachine çıkışları hata ayıklama konsolunda görüntülenir.

Diğer giriş durumlarının nasıl etkilendiğini merak ediyorsanız dönüşümden önce diğer kubit işlemlerini uygulamayı deneyin.

QFT bağlantı hattına ölçüm ekleme

DumpMachine işlevinden görüntülenen görüntüde işlemin sonuçları gösterilir, ancak ne yazık ki kuantum mekaniğinin bir köşe taşı, gerçek bir kuantum sisteminin böyle bir DumpMachine işlevine sahip olmadığını belirtir. Bunun yerine, bilgiler ölçümler aracılığıyla ayıklanır ve genel olarak sadece tam kuantum durumu hakkında bilgi sağlamamakla kalmaz, aynı zamanda sistemi de önemli ölçüde değiştirebilir.

Birçok tür kuantum ölçümü vardır, ancak buradaki örnek en temel ölçümlere odaklanır: tek kubitlerdeki yansıtıcı ölçümler. Belirli bir temelde ölçüldükten sonra (örneğin, hesaplama temeli $ { \ket{0}, \ket{1} } $), kubit durumu ölçülen temel duruma yansıtılır ve bu nedenle ikisi arasındaki süper konum yok edilir.

QFT işlemini değiştirme

Bir Q# programı içinde ölçümler uygulamak için, M türünü döndüren Result işlemini kullanın.

İlk olarak, Main işlemini Result[] yerine bir ölçüm sonuçları dizisi döndürecek şekilde değiştirin Unit.

operation Main() : Result[] {

Dizi tanımlama ve başlatma Result[]

Kubitleri ayırmadan önce üç öğeli bir dizi (her kubit için bir tane Result ) bildirin ve bağlayın:

mutable resultArray = [Zero, size = 3];

Anahtar sözcük mutable başındaki resultArray , değişkenin kodda daha sonra değiştirilmesini sağlar, örneğin ölçüm sonuçları eklenirken.

Döngüde for ölçümler yapma ve diziye sonuç ekleme

QFT dönüştürme işlemlerden sonra aşağıdaki kodu ekleyin:

for i in IndexRange(qs) {
    resultArray w/= i <- M(qs[i]);
}

Bir IndexRange dizide çağrılan işlev (örneğin kubit dizisi), qsdizinin dizinleri üzerinde bir aralık döndürür. Burada, for deyimini kullanarak her kubiti sıralı olarak ölçmek için M(qs[i]) döngüsünde kullanılır. Ölçülen her Result türü (ya Zero ya da One), 'resultArray' içindeki ilgili dizin konumuna bir güncelleme ve yeniden atama ifadesi ile eklenir.

Not

Bu deyimin söz dizimi Q# için benzersizdir, ancak F# ve R gibi diğer dillerde görülen benzer değişken yeniden atama yapısına resultArray[i] <- M(qs[i]) karşılık gelir.

Anahtar sözcük set, mutable ile bağlı değişkenleri yeniden atamak için her zaman kullanılır.

Dönmek resultArray

Üç kubitin de ölçülmesi ve sonuçların resultArray'a eklenmesiyle, kubitleri daha önce olduğu gibi sıfırlayabilir ve serbest bırakabilirsiniz. Ölçümleri döndürmek için şunu ekleyin:

return resultArray;

Ölçümlerle QFT devresini çalıştırma

DumpMachine işlevlerinin yerleşimini şimdi değiştirerek ölçümlerin öncesi ve sonrası durumu çıktıya verin. Son Q# kodunuz şu şekilde görünmelidir:

import Std.Diagnostics.*;
import Std.Math.*;
import Std.Arrays.*;

operation Main() : Result[] {

    mutable resultArray = [Zero, size = 3];

    use qs = Qubit[3];

    //QFT:
    //first qubit:
    H(qs[0]);
    Controlled R1([qs[1]], (PI()/2.0, qs[0]));
    Controlled R1([qs[2]], (PI()/4.0, qs[0]));

    //second qubit:
    H(qs[1]);
    Controlled R1([qs[2]], (PI()/2.0, qs[1]));

    //third qubit:
    H(qs[2]);

    SWAP(qs[2], qs[0]);

    Message("Before measurement: ");
    DumpMachine();

    for i in IndexRange(qs) {
        resultArray w/= i <- M(qs[i]);
    }

    Message("After measurement: ");
    DumpMachine();

    ResetAll(qs);
    Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
    return resultArray;

}

İpucu

Yeniden çalıştırmadan önce kodda her değişiklik eklediğinizde dosyanızı kaydetmeyi unutmayın.

  1. Programınızı çalıştırmak için, önündeki Q#menüden seçin veya Main tuşlarına basın. Program, Main işlemi varsayılan simülatörde çalıştırır.
  2. Message ve DumpMachine çıkışları hata ayıklama konsolunda görüntülenir.

Çıktınız şuna benzer görünmelidir:

Before measurement: 

 Basis | Amplitude      | Probability | Phase
 -----------------------------------------------
 |000⟩ |  0.3536+0.0000𝑖 |    12.5000% |   0.0000
 |001⟩ |  0.3536+0.0000𝑖 |    12.5000% |   0.0000
 |010⟩ |  0.3536+0.0000𝑖 |    12.5000% |   0.0000
 |011⟩ |  0.3536+0.0000𝑖 |    12.5000% |   0.0000
 |100⟩ |  0.3536+0.0000𝑖 |    12.5000% |   0.0000
 |101⟩ |  0.3536+0.0000𝑖 |    12.5000% |   0.0000
 |110⟩ |  0.3536+0.0000𝑖 |    12.5000% |   0.0000
 |111⟩ |  0.3536+0.0000𝑖 |    12.5000% |   0.0000

After measurement: 

 Basis | Amplitude      | Probability | Phase
 -----------------------------------------------
 |010⟩ |  1.0000+0.0000𝑖 |   100.0000% |   0.0000

Post-QFT measurement results [qubit0, qubit1, qubit2]: 

[Zero, One, Zero]

Bu çıkış birkaç farklı şeyi gösterir:

  1. Ön ölçümle karşılaştırıldığında, elde edilen sonuç, temel durumlar üzerinde QFT sonrası süperpozisyonu açıkça göstermez. Ölçüm, sistemin dalga işlevindeki bu durumun genliği tarafından belirlenen bir olasılıkla yalnızca tek bir temel durum döndürür.
  2. Ölçüm DumpMachinesonrasında ölçüm, durumu değiştirir ve temel durumlar üzerinden ilk süper pozisyondan ölçülen değere karşılık gelen tek temel duruma yansıtılır.

Bu işlemi birçok kez tekrarlarsanız, sonuç istatistikleri her çekimde rastgele bir sonuca neden olan QFT sonrası durumun eşit ağırlıklı süperpozisyonunu göstermeye başlar. Ancak, verimsiz ve hala kusurlu olmasının yanı sıra, bu, yalnızca temel durumların göreli genliklerini yeniden üretir, durumlar arasındaki göreli fazları değil. Bu örnekte bu durum bir sorun teşkil etmez, ancak QFT'ye $\ket{000}$ durumundan daha karmaşık bir giriş verildiğinde göreli fazlar görüntülenir.

QFT devresini basitleştirmek için Q# işlemlerini kullanın

Girişte belirtildiği gibi, Q#'nin gücünün çoğu, bireysel kubitlerle ilgilenme endişelerini ortadan kaldırmanıza olanak sağlaması gerçeğinden gelmektedir. Aslında, tam ölçekli, geçerli kuantum programları geliştirmek istiyorsanız, bir işlemin belirli bir H döndürmeden önce mi yoksa sonra mı geçeceği konusunda endişelenmeniz sizi yalnızca yavaşlatır. Azure Quantum, istediğiniz sayıda kubit için kullanabileceğiniz ve uygulayabileceğiniz işlemi sağlar ApplyQFT .

  1. İlk H işlemden SWAP işlemine kadar her şeyi, dahil, şununla değiştirin:

    ApplyQFT(qs);
    
  2. Kodunuz şimdi şöyle görünmelidir

    import Std.Diagnostics.*;
    import Std.Math.*;
    import Std.Arrays.*;
    
    operation Main() : Result[] {
    
        mutable resultArray = [Zero, size = 3];
    
        use qs = Qubit[3];
    
        //QFT:
        //first qubit:
    
        ApplyQFT(qs);
    
        Message("Before measurement: ");
        DumpMachine();
    
        for i in IndexRange(qs) {
            resultArray w/= i <- M(qs[i]);
        }
    
        Message("After measurement: ");
        DumpMachine();
    
        ResetAll(qs);
        Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
        return resultArray;
    
    }
    
  3. Q# Programı yeniden çalıştırın ve çıkışın öncekiyle aynı olduğuna dikkat edin.

  4. Q# işlemlerini kullanmanın gerçek avantajını görmek için kubit sayısını, 3 dışındaki bir değere değiştirin.

mutable resultArray = [Zero, size = 4];

use qs = Qubit[4];
//...

Böylece, her kubite yeni H işlemler ve döndürmeler ekleme konusunda endişelenmenize gerek kalmadan, belirli sayıda kubit için uygun QFT'yi uygulayabilirsiniz.

Diğer Q# öğreticileri keşfedin: