Jednym z fundamentalnych zjawisk występujących w fizyce kwantowej oraz w programowaniu na komputerach jest splątanie kwantowe. Zjawisko to występuję w przypadku dwóch kwantów które są splątane i powoduję ze dokonanie pomiaru na jednym z tych kubitów nie tylko określi wartość dla tego kubita ale również spowoduje określenie wartości dla drugiego kubita i to natychmiastowo. Niezależnie od odległości jaka się między nimi znajduję. Do splątania kubitów najczęściej wykorzystuję się bramkę CNOT opisaną we wcześniejszym artykule. Niemniej nic nie stoi na przeszkodzie by dokonać splątania większej liczby kubitów w bardziej zaawansowanych algorytmach.
Tak jak we wcześniejszym artykule tworzymy hosta będącego klasycznym komputerem odpytującym bibliotekę kwantową i oczekiwał na wyniki.
using Microsoft.Quantum.Simulation.Simulators;
using Quantum.QuantumDreamLibrary;
using System;
namespace QuantumDream
{
static class Program
{
static void Main(string[] args)
{
using (var sim = new QuantumSimulator())
{
var result = EntangleTest.Run(sim,10000).Result;
var (numZeros1, numOnes1, numZeros2, numOnes2) = result;
Console.WriteLine($"Zero1 {numZeros1}, One1 {numOnes1}, Zero2 {numZeros2}, One2 {numOnes2}");
Console.WriteLine("Press any key to continue");
Console.ReadKey();
}
}
}
}
var result = EntangleTest.Run(sim,10000).Result;
W metodzie run przekazujemy symulator oraz parametry potrzebne do rozpoczęcia operacji. W tym wypadku operacje powtarzamy 10000 razy.
var (numZeros1, numOnes1, numZeros2, numOnes2) = result;
Odczytujemy uzyskane wyniki.
Kod kwantowy zaś wygląda następująco:
namespace Quantum.QuantumDreamLibrary {
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
@EntryPoint()
operation EntangleTest(count : Int) : (Int, Int, Int, Int) { //operacja przyjmuje tylko jeden parametr odpowiedzialny za ilość pomiarów, zwracane są cztery wartości Int {
mutable numOnesQubit1 = 0; // licznik wystąpienia 1 dla pierwszego kubita
mutable numOnesQubit2 = 0; // licznik wystąpienia 1 dla drugiego kubita
use (qubit1, qubit2) = (Qubit(), Qubit()); //alokowanie 2 kubitów
for i in 1..count {
SetQubitState(Zero, qubit1); //zerowanie qubitow przed ponowną operacją
SetQubitState(Zero, qubit2);
H(qubit1); //użycie bramki Hadamara w celu ustawienia kubita w superpozycji
CNOT(qubit1, qubit2); //użycie bramki CNOT w celu powiązania kubitów
let resultQubit1 = M(qubit1); //dokonanie pomiaru na 1 kubicie, powoduje ustalenie wartości dla qubita1 oraz qubita2
let resultQubit2 = M(qubit2); //dokoanienie pomiaru 2 kubita który ma już ustaloną wartość
if resultQubit1 == One {
set numOnesQubit1 += 1;
}
if resultQubit2 == One {
set numOnesQubit2 += 1;
}
}
// restart kubitow do stanu |0>
SetQubitState(Zero, qubit1);
SetQubitState(Zero, qubit2);
// Zwracane wartości
Message("q1:Zero, One q2:Zero, One");
return (count - numOnesQubit1, numOnesQubit1, count - numOnesQubit2, numOnesQubit2 );
}
//operacja ustawiająca stan kubita na |0> lub |1>
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) { // dokonanie pomiaru i sprawdzenie czy wartość kubita jest zgodna z oczekiwaniem
X(target); //użycie bramki Not (pauli gate X) w celu odwrócenia wartości
}
}
}
Powinniśmy otrzymać taką samą liczbę jedynek i zer dla pierwszego i drugiego kubita. Prawdopodobieństwo dla pierwszego i drugiego wyniku jest równe 50% więc wyniki dla obydwu wartości będą bliskie 5000.
Projekt dostępny jest do pobrania tutaj