量子計算實戰:IBM Q Experience 和量子猜球游戲
一起來玩這個游戲!想看看如何在實際生活中玩這個游戲? 在這里試試!
您可以玩玩這個經典的猜球游戲,并查看 QASM 代碼和執行結果。祝您玩得愉快!
我在博客文章 通過量子計算、量子比特和 IBM Q 解決猜球游戲問題 中提到過,一次高中同學聚會上與好友的一番對話,引起了我對如何使用量子計算解決實際問題的思考。作為對我自己的挑戰,我想使用 IBM 的量子技術(具體來講是 IBM Q Experience)重新創建經典的猜球游戲。
在這個猜球游戲中,用戶選擇要將球藏在一個盒子下,然后讓量子計算機猜測是藏在哪個盒子下。在本教程中,將介紹我使用 IBM Q Experience 創建的猜球游戲版本的步驟。
預備知識
如果您有一點創建網站的經驗,而且比較熟悉 HTML 和 JavaScript,您會發現這是一次非常簡單(而輕松)的體驗。我不會介紹 C#、HTML 或 JavaScript 代碼的細節,因為這與如何使用 Q Experience 平臺沒有多大關系。您會看到,棘手的部分是后端發生的事情。
如果您對 IBM Q Experience 的 Quantum Composer 和 Quantum Assembler 的性質有一定的了解,也會對您有所幫助 - 但這不是理解這個簡單的操作教程所必需的。
系統要求
首先,您需要有一個 IBM Q Experience 帳戶。這個帳戶是免費的!是不是很不錯?要進行注冊,請 訪問 Quantum experience 頁面 。
您需要一個集成開發編輯器 (IDE) 來編寫代碼。出于我的目的,我使用了 Microsoft Visual Studio 2015。如果您非要堅持的話,您可以使用記事本,但我不推薦使用它。
您可以在自己的系統上本地運行實驗,或者在您選擇的云平臺上運行實驗。我已將我的代碼發布到 IBM Cloud 上,以便與您分享。
一種解決方法
在思考如何使用量子計算解決我的難題時,我首先將此項目構想為一個完全基于瀏覽器的 JavaScript 任務。我編寫了一些 HTML 5 代碼,添加了一些出色的 JS 功能,讓這些盒子會在屏幕上移動。我可以在 JavaScript 中構建量子匯編代碼 (QASM),并通過一個簡單的 Web 服務調用將它傳遞給 IBM Q Experience 平臺。
不幸的是,IBM 不允許從外部域進行跨來源資源共享 (CORS),所以瀏覽器阻止了我們執行任何服務調用。讓人焦頭爛額!
其實,沒那么糟糕。但讓我們回到起點。
另一種替代方案是從服務器端代碼發出請求,而不是從無法應用 CORS 的瀏覽器發出請求。這使情況變得有點復雜。
現在,我們的猜球游戲必須將它的布局提交給服務器。服務器將請求匯編為 QASM 代碼并提交給 IBM 的平臺。然后,從這個 Web 服務請求返回的 JSON 經過服務器解析并反饋到網頁上,在其中顯示量子計算機選擇了哪個盒子。基本來講,我們插入了一個中間媒介來解決該問題。很快,游戲就成功實現了。
量子解決方案
可以通過多種方式創建、編輯 ASP.NET Core 應用程序并將它部署到 IBM Cloud。我首先在 Visual Studio 中創建一個 ASP.NET Core 1.0 項目。刪除模板中默認包含的 Contact 和 About 頁面后,我修改了 Views\Shared\_Layout.cshtml 文件和 Views\Home\Index.cshtml,使它們符合我的要求。
然后,我在 Controllers 目錄下創建了一個文件夾來存儲一些類文件,這些文件將用于與 IBM Q 平臺進行通信。
圖 1. ASP.NET Core 解決方案
我們首先將注意力集中在 IBMQ 文件夾上,因為它是這個程序的真正核心。
這個文件夾中的類負責登錄到 IBM Q Experience 平臺,匯編量子匯編代碼 (QASM),對 IBM 的量子處理器執行該代碼,并解析執行的結果。執行和解析代碼是通過公開的 Web 接口完成的。
- QProcessor 類是此操作的核心。它處理登錄功能和 QASM 代碼的執行,以及兩個實用工具和清理例程。它還持久保存執行各種 Web 服務調用所需的安全信息,并包裝實現。請注意用于刪除實驗的函數。IBM Q Experience 平臺將每次執行都保存為一次實驗 - 隨著時間的推移,尤其是在測試某些想法時,您的 Q Experience 平臺上的帳戶下執行的實驗將會泛濫成災。我發現最佳的管理方式是在運行實驗后立即將其刪除。
- QResult 類是一個簡單的構造方法,用于在組件之間來回傳遞一次操作的成功情況。
- QUser 類是一個對象,用于保存登錄 Q Experience 平臺的結果。在本實驗中,您需要在登錄后記錄用戶的 userId,因為以后會使用此信息作為訪問令牌,向您授予對帳戶執行操作的權限
- QCode 類將用于匯編 QASM 代碼。它也包含嘗試次數(可執行量子運算的次數)。請記住,返回的量子比特是一個為 0 或 1 的概率。在這個應用程序中,我嘗試運行該量子代碼 500 次(這足以收斂到 1 或 0 的近 100% 概率),這從一定程度上消除了量子處理的乏味。
- QExecutionOutput 是這 5 個類中最復雜的。但是,出于本應用程序的目的,我們將忽略它的大部分內容。最重要的部分是隱藏在 Data 類中的 P 類。有兩個屬性(labels 和 values)可以保存結果。labels 屬性告訴我們一個給定的量子比特得到了經典二進制位的 1 還是 0,values 屬性告訴我們得到該結果的概率。該類本身是從對 IBM 平臺執行樣本代碼的 JSON 結果中生成的。我使用了一個在線轉換器(在本例中為 json2csharp.com )來構建 QExecutionOutput 類。無可否認,此類或許不是處理 JSON 結果的完美方式,但它比手動編寫類更快。
讓我們分析一下這些類將執行的步驟。
與 IBM Q Experience 平臺進行通信
我們需要做的第一件事是將數據發送到 IBM Q Experience 平臺。無需反復編寫和重寫相同的代碼,我將它組合成一個名為 FetchAPIData 的函數。這樣,PerformLogin 等其他函數只需要傳遞相對 URL、一個 HTTP 方法(post、delete、get 等)和內容(如果提交)。FetchApiData 會完成剩余工作。
它首先會將該 URL 的相對部分添加到基礎 URL 中:https://quantumexperience.ng.bluemix.net/api。如果我們沒有執行刪除,而且我們有一個 User 對象,它還將傳遞 User.id 作為 access_token。當執行刪除時,我們傳遞 User.id 作為請求標頭 (X-Access-Token),而不將 access_token 附加到 URL。
下面這段代碼只構建請求消息,添加內容,然后設置請求標頭。如果我們獲得了成功狀態代碼,則證明我們正確地發送了 QResult 對象,任務完成。
清單 1. 發送我們的 QResult 對象
private QResult FetchAPIData(string urlRelativePath,
HttpMethod httpMethod,
HttpContent contentToSend)
{
QResult result = new QResult();
//add auth token if we have a user and we arent deleting
if (User != null && httpMethod !=HttpMethod.Delete)
{
urlRelativePath = urlRelativePath + "&access_token=" + User.id;
}
string url = _baseUrl + urlRelativePath;
Debug.WriteLine("Performing " + httpMethod.ToString() + " to " + url);
if (contentToSend!=null) {
Debug.WriteLine("Sending data " + contentToSend.ReadAsStringAsync().Result);
}
HttpRequestMessage request = new HttpRequestMessage(httpMethod, url);
request.Content = contentToSend;
if (User != null) request.Headers.Add("X-Access-Token", User.id);
using (HttpResponseMessage response = _client.SendAsync(request).Result)
if (response.IsSuccessStatusCode)
{
using (HttpContent content = response.Content)
{
// ...Read the string.
result.Message = content.ReadAsStringAsync().Result;
result.Success = response.IsSuccessStatusCode;
}
} else
{
result.Message = response.ReasonPhrase;
result.Success = false;
}
return result;
}
就這么簡單!砰!
現在我們已確定如何與 IBM 平臺進行通信,接下來需要理解我們必須傳遞什么信息。幸運的是,這非常簡單 - 分兩個階段。
- 第一階段是交換憑證。
- 第二階段是傳輸 QASM 代碼本身,并接受表示量子代碼執行結果的 JSON 數據。
第 1 階段.交換憑證
讓我們從處理憑證開始。對于 IBM Q Experience 平臺,我們需要一個 API 密鑰。要獲取此密鑰,請執行以下操作:
- 轉到您的 IBM Quantum experience 帳戶頁面 。
- 單擊 Regenerate 按鈕,以便為您創建一個 API 密鑰。
圖 2. IBM 的 Q Experience 概況頁面中的 API 令牌
- 有了 API 令牌后,將它傳遞給位于“/users/loginWithToken”端點上的 Web 服務。下面的代碼清單給出了我們用于完成此任務的 PerformLogin 例程。
清單 2. 傳遞 API 令牌
private QResult PerformLogin()
{
if (_token == string.Empty) throw new Exception("A token is required.");
QResult result = new QResult();
HttpContent content= new StringContent("apiToken=" + _token,
System.Text.Encoding.UTF8,
"application/x-www-form-urlencoded");//CONTENT-TYPE header
result = FetchAPIData("/users/loginWithToken", HttpMethod.Post, content);
if (result.Success)
{
User = JsonConvert.DeserializeObject<QUser>(result.Message);
Debug.WriteLine("Logged in and have UserID: " + User.userid);
} else
{
User = null;
}
return result;
}
這里只有一個需要注意的地方:您沒有將 JSON 數據提交到此端點,而是提交了 application/x-www-form-urlencoded 數據。這需要花點精力才能理解 - 同樣幸運的是,Fiddler 和 Wireshark 是對這些功能執行逆向工程的好工具。
如果令牌經過了驗證,我們會獲得類似下面這樣的 JSON 包:
清單 3. 返回的 JSON 包
"{\"id\":\"XXXXXXXXXXXXXXXXXXXXXXXXXX\",\"ttl\":1209600,\"created\":
\"2017-10-03T14:51:51.918Z\",\"userId\":
\"XXXXXXXXXXXXXXXXXXXXXXXXXXX7999a6\"}"
在對 IBM 平臺的后續調用中會使用 userId 字段的值。該值在存活時間 (ttl) 過期之前工作正常 - 在這段簡單的演示代碼中,我們忽略了這一點。所以,在我們的登錄步驟中,我們對結果消息數據中的 userId 進行了反序列化,并將結果隱藏在我們的全局 user 對象中。
第 2 階段.執行代碼
現在是時候提交 QASM 代碼來執行它了。我們將創建一個包含以下信息的 ExecuteCode 函數:
- 一個包含以下 3 個參數的 URL 字符串:
- Shots:要執行實驗的次數
- Seed
- deviceRunType:我們使用了“simulator”,但是,如果您愿意的話,可以在真正的量子處理器上運行此代碼
- 其他數據包括:
- QASM 代碼本身
- codeType:對我們而言,代碼類型為 QASM2
- 分配給這次運行的名稱
下面是完整的 ExecuteCode 函數:
清單 4. ExecuteCode 函數
public QResult ExecuteCode(QCode code)
{
if (this.User == null) throw new Exception("Not logged in.");
QResult result = new QResult();
string url = string.Format("/codes/execute?shots={0}&seed={1}&deviceRunType={2}",
code.shots.ToString(),
code.seed.ToString(),
code.deviceRunType
);
string data = string.Format("qasm={0}&codeType={1}&name={2}",
code.qasm,
code.codeType,
code.name
);
var kvp = new List<KeyValuePair<string, string>>();
kvp.Add(new KeyValuePair<string, string>("qasm", code.qasm));
kvp.Add(new KeyValuePair<string, string>("codeType", code.codeType));
kvp.Add(new KeyValuePair<string, string>("name", code.name));
HttpContent content = new FormUrlEncodedContent(kvp);
result = FetchAPIData(url, HttpMethod.Post, content);
Debug.WriteLine("ExecuteCode received the following JSON from the API:");
Debug.WriteLine(result.Message);
return result;
}
我們將數據打包到一個 KeyValuePair 中,然后對它執行 FormUrlEncode,以便可以通過 FetchAPIData 調用,使用 POST 將它提交到該 URL。
不錯的開端。但是現在該做什么?量子處理器如何解決猜球游戲?
Grover 算法和振幅放大技巧
要理解量子計算的工作原理,需要了解 Grover 算法和振幅放大技巧
如果您需要更詳細的解釋,IBM Q Experience 中的 Grover 算法教程 透徹地剖析了該算法。
在物理現實中,如果向您提供一組盒子,您會逐個翻開它們,直到找到球。在計算機世界中,這個猜球游戲只是一種非結構化搜索。
在非結構化搜索流程中,通過對一個集合內的每個元素應用布爾運算(一種具有明確的 f(x)=1 形式的函數),對該集合內的 N 個元素進行搜索。
給定一個包含 N 個元素的集合 X ={x1, x2,…xN} 并給定一個函數 f:X=>{0,1},找到 X 中滿足條件 f(x*)=1 的元素 x*。
在我們的例子中,我們有一些盒子(假設數量為 N),我們想找到下面藏有球的盒子。我們選擇一個盒子時,如果它下方有一個球,則會得到結果“true”或“1”。如果我們在盒子下找到球以外的任何東西,那么結果將為值“false”或“0”。通過這種方式,如果在盒子下找到球,函數表示為 f(x) = 1,如果不是球,函數表示為 f(x) = 0。
在經典計算領域和我們的猜球游戲中,我們讀取每個盒子,應用該函數,直到找到球。這有時稱為“占卜”,因為我們將該函數視為一個黑盒。我們沒有必要關心盒子內是什么,只需關心函數的結果是 1 還是 0。給定 N 個盒子,經典計算機的最糟糕性能為 O(N) 次運算(例如,球在最后的盒子下)。
使用量子計算機和振幅放大技巧,我們可以在 O(sqr(N)) 次運算內解決此問題。但是,量子計算從來都不是那么簡單。量子計算得到一個小于 1 的概率 - 而不是準確的答案。即便如此,我們可以多次運行計算,直到結果是任何一個隨意選擇的高概率(比如 0.99 或更高)。出于我們的目的,我們將進行 500 次嘗試。該計算幾乎總是得到接近 100% 的概率(或者至少可以舍入為 100% 的概率)。
映射
請記住,我們希望將真實問題映射到量子處理器的物理狀態,執行邏輯,然后將結果逆向映射回真實問題。
那么在我們的猜球游戲中,我們需要:
- 將用戶選擇的盒子數量傳遞到后端。
- 讓后端將該數據以 QASM 代碼形式傳遞給量子處理器,由該處理器執行 Grover 算法。
- 檢索并解析這些結果。
- 將結果數據映射回真實問題(例如顯示用戶選擇了哪個盒子)。
呼!很難講清,但還沒有難到講不清。在實例化期間,QCode 類執行了所有工作。在這里,我們將逐個步驟進行剖析。
第 1 步.將用戶選擇的盒子數量傳遞到后端
我們向 QCode 構造方法傳遞一個參數,表明用戶選擇了哪個盒子。這會指導創建 QASM 代碼來正確反映實際環境與量子處理器的執行環境的映射。QCode 構造方法代碼首先設置 QASM 代碼的第一部分,這部分建立 5 個量子比特和 5 個經典寄存器。
清單 5. 設置 QASM 代碼
//5 qubits and 5 classical registers
string preCode = "include \"qelib1.inc\";qreg q[5];creg c[5];";
為什么是 5 個?說實話,我們只需要兩個,我們使用 5 個是為了讓此演示與 Q Experience 網站上的 教程 中提供的 Composer 分數保持一致。
第 2 步.讓后端將該數據以 QASM 代碼形式傳遞給量子處理器,由該處理器執行 Grover 算法。
我們為這些量子比特分配狀態。系統根據我們嘗試表示哪個狀態來構建 QASM。請記住,如果用戶選擇了盒子 #1,我們希望將量子比特的狀態設置為“00”狀態。盒子 #2 的狀態為“01”,依此類推。switch 語句負責設置這些狀態。下面的代碼清單展示了如何分配這些狀態:
清單 6. 分配狀態
switch (coinUnderShellNumber)
{
case 1: //#State=00
initCode = @"h q[1];
h q[2];
s q[1];
s q[2];
h q[2];
cx q[1],q[2];
h q[2];
s q[1];
s q[2];
";
break;
case 2: //#State=01
initCode = @"h q[1];
h q[2];
s q[2];
h q[2];
cx q[1],q[2];
h q[2];
s q[2];";
break;
case 3: //#State=10
initCode = @"h q[1];
h q[2];
s q[1];
h q[2];
cx q[1],q[2];
h q[2];
s q[1];";
break;
case 4: //#State=11
initCode = @"h q[1];
h q[2];
h q[2];
cx q[1],q[2];
h q[2];
";
break;
default:
throw new Exception("There can only be 4 shells.");
}
最后,我們將 Grover 算法添加到這段 QASM 代碼的末尾,如下面的代碼清單所示:
清單 7. 添加 Grover 算法
//everything starting at the "double Hadamard"
string postControlledNot = @"h q[1];
h q[2];
x q[1];
x q[2];
h q[2];
cx q[1], q[2];
h q[2];
x q[1];
x q[2];
h q[1];
h q[2];
measure q[1] -> c[1];
measure q[2] -> c[2];";
我們將所有這些信息組合為一個字符串:
清單 8. 組合為一個字符串
this.qasm = preCode + initCode + postControlledNot;
將嘗試次數設置為 500,以便我們的實驗運行次數足以收斂到這些概率。
清單 9. 設置嘗試次數
this.shots = 500;
第 3 步.檢索并解析這些結果
我們之前討論的 ExecuteCode 函數負責將代碼傳遞到 Q Experience 平臺。如果代碼成功執行,我們會獲得類似下面的代碼清單的 JSON:
清單 10. JSON 輸出
{"result":{"date":"2017-10-
03T17:07:21.819Z","data":{"creg_labels":"c[5]","p":
{"qubits":[1,2],"labels":["00110"],"values":[1]},
"additionalData":{"seed":1},"qasm
...
,"userDeleted":false,"id":"7012d29df79ba1fca3e5eda009069d3d",
"userId":"a3e5c196cb90688ba9a50dd7607999a6"}}
該 JSON 輸出中隱藏的“labels”字段中包含我們的答案 - 在本例中為“00110”(已在上面突出顯示)。“values”字段列出了該答案的概率。因為我們運行了 500 次模擬,所以最終結果非常接近 100%,足以讓結果舍入為 1。因此,解析該結果很簡單,只需將該 JSON 合成為一個對象并讀取 labels 屬性:
清單 11. 解析結果
QExecutionOutput x= JsonConvert.DeserializeObject
<QExecutionOutput>(result.Message);
Debug.WriteLine("The values are: " + x.result.data.p.labels[0]);
第 4 步.將結果映射回真實問題
將結果映射回真實問題是另一個 switch 語句,如下面的代碼清單所示:
清單 12. 映射結果
//parse the result and output the location of the coin
QExecutionOutput x = qp.GetOutputFromMessageData(result.Message);
string labels = x.result.data.p.labels[0];
switch (labels)
{
case "00000":
Debug.WriteLine("The coin was under Shell #1");
ComputedShell.Value = "1";
break;
case "00100":
Debug.WriteLine("The coin was under Shell #2");
ComputedShell.Value = "2";
break;
case "00010":
Debug.WriteLine("The coin was under Shell #3");
ComputedShell.Value = "3";
break;
case "00110":
Debug.WriteLine("The coin was under Shell #4");
ComputedShell.Value = "4";
break;
default:
Debug.WriteLine("Something broke!");
ComputedShell.Value = "0";
break;
}
如果您僅嘗試了兩次,可能會獲得完全不同的結果。 或許我們應檢查 values 數組并選擇具有最高概率的數組索引,然后選擇具有匹配的數組索引的標簽作為答案。但是,因為我們運行了 500 次,所以我們的結果幾乎總是只有 1 個答案的數組。就像下面的 JSON 中一樣:
”qubits":[1,2],"labels":["00110"],"values":[1]},”
概率被舍入為 1,而且只有一個標簽“00110”,所以在我們的猜球游戲中,我們僅讀取了索引 0 處的結果。在本例中,球隱藏在第 4 個盒子下!
總結
在我們的 ASP.NET 網頁中,當用戶按 Submit 按鈕時,會在 ShellGameModel 類的“ShellSelected”屬性中傳回盒子的編號。我們讀取此值,創建我們的 QProcessor 對象(使用我們的令牌將其實例化)。然后登錄。
如果登錄成功,我們將實例化我們的 QCode 對象,向它傳入所選的盒子的值。然后,我們將代碼傳遞給 ExecuteCode 函數。我們利用該代碼創建了一個 QExecutionOutput 對象,并讀取結果的 labels 屬性。
請記住,我們的 labels 是以經典方式表達的量子比特狀態(1 或 0)。通過另一個簡單的 switch 語句,我們可以傳回量子處理器能夠推斷出的球位置信息。
清單 13. 查找球的位置
[HttpPost]
public IActionResult Index(ShellGameModel model)
{
model.ComputedShell = "0";
model.QASM = "";
model.ExecutionResults = "";
string token = "INSERTYOURTOKENHERE";
//Build the processor
QProcessor qp = new QProcessor(token);
//login
QResult result = qp.Login();
Debug.WriteLine(string.Format("Login result. Success={0} Message={1}", result.Success.ToString(), result.Message));
if (result.Success)
{
int shell = 0;
Int32.TryParse(model.ShellSelected, out shell);
//build the QASM code
QCode code = new QCode(shell);
code.name = string.Format("ExperimentID {0} with Shell at {1} ", System.Guid.NewGuid().ToString(), shell.ToString());
Debug.WriteLine("Code:"+ Environment.NewLine + code.qasm);
//execute the code
result = qp.ExecuteCode(code);
Debug.WriteLine(string.Format("Code Executed Success={0}, Data={1}", result.Success.ToString(), result.Message));
//parse the result and output the location of the coin
QExecutionOutput x = qp.GetOutputFromMessageData(result.Message);
string labels = x.result.data.p.labels[0];
switch (labels)
{
case "00000":
Debug.WriteLine("The coin was under Shell #1");
model.ComputedShell = "1";
break;
case "00100":
Debug.WriteLine("The coin was under Shell #2");
model.ComputedShell = "2";
break;
case "00010":
Debug.WriteLine("The coin was under Shell #3");
model.ComputedShell = "3";
break;
case "00110":
Debug.WriteLine("The coin was under Shell #4");
model.ComputedShell = "4";
break;
default:
Debug.WriteLine("Something broke!");
model.ComputedShell = "0";
break;
}
model.QASM = JsonConvert.SerializeObject(x.code, Formatting.Indented);
model.ExecutionResults = JsonConvert.SerializeObject(x.result, Formatting.Indented);
//now cleanup and delete the results
QResult deleteResult = qp.DeleteExperiment(x.code.idCode);
}
return View(model);
}
瞧!我們將猜球游戲的實際問題映射到了代碼中,在量子處理器上執行了它,處理了結果并將解決方案映射回實際環境中。
這可能不是您見過的最出色的工作(我可以想到 100 種改進它的方式),但它簡單有效,而且足夠表達本演示的目的。
親自玩玩該游戲
想看看如何在實際生活中玩這個游戲? 玩游戲 并查看 QASM 代碼和執行結果。祝您玩得愉快!
結束語
感謝您跟我一起學習了 IBM Q Experience 的教程。嘗試這項實驗性技術很有趣,而且可以使用它創建有趣的小游戲。希望您能夠利用本教程中的知識,使用這項量子技術親自創建游戲或解決難題。
后續行動
- 量子計算初學者指南:在這個初學者為其他初學者編寫的指南中,Talia Gershon 博士向開發人員解釋了量子計算的基礎操作。
- 量子計算:IBM Q Experience 的初學者筆記和概述 :在這篇博客文章中,Kevin Casey 介紹了他第一次使用 IBM Q Experience 的經歷。
- IBM Q:進一步了解量子計算,閱讀教程,并親自實驗該技術。
- QISKit API:輕量型、開源的 QISKit API 是一個圍繞 IBM Quantum experience HTTP API 的瘦 Python 包裝器,它使您能夠連接和執行 OPENQASM 代碼。
來自:http://www.ibm.com/developerworks/cn/opensource/os-quantum-computing-shell-game/index.html?ca=drs-