# 基礎知識
* $|0> = \begin{bmatrix} 1 \\ 0 \end{bmatrix} = | \uparrow >$
$|1> = \begin{bmatrix} 0 \\ 1 \end{bmatrix} = | \downarrow >$
* $|0> \xrightarrow{H \ gate} |+> \equiv \cfrac{1}{\sqrt{2}}(|0>+|1>)$
$|1> \xrightarrow{H \ gate} |-> \equiv \cfrac{1}{\sqrt{2}}(|0>-|1>)$
$\Longrightarrow H \ gate = Ry(\cfrac{\pi}{2}) \ast Rx(\pi)$
* 創建 Bell State (糾纏態)

1. 第一步 : 放入 H gate : $\cfrac{1}{\sqrt{2}}(|0>+|1>) \otimes |0> = \cfrac{1}{\sqrt{2}}(|00>+|01>)$
2. 第二步 : 放入 CNOT gate : $\cfrac{1}{\sqrt{2}}(|00>+|11>)$
這就是 Bell State 了,因為不能被拆解成 2 位元的 "張量積態" (tensor product state) 所以是一種糾纏態
* $\otimes$ 的算法是這樣 :

# 名詞翻譯
* spherical coordinates : 球座標
* retrieves : 檢索
* monitors : 監測
* fidelity : 保真度
* depicts : 描述
* global phase : 全局變量
# 電路程式觀念
* `qc.compose(qc要結合到的地方,{可多加一項 front = True表示加在要結合的電路的前面})` : 結合電路
* `qiskit.__version__` 或 `qiskit.__qiskit_version__` : 是查看 qiskit 的版本
* `job_monitor` : 監測現在的工作狀態
* `Aer.get_backend("qasm_simulator")` : 獲取名叫 qasm_simulator 的後端
* `operator(qc)` : 把電路轉為矩陣
* `qc.to_gate` : 把電路轉為可以重複使用的 gate
* `qc.append(HGate(),[0])` : 在編號 0 的 qubit 中放入 H 閘,並且直接應用,沒有轉換
* `execute(...).result().get_memory()` : 最後面的`get_memory`代表需要返回測量結果
* `execute(qc, sim, basic_gates = [u, cx])` : 在 sim 這個模擬器後端中執行 qc 這段電路後,展開到 u 閘 & cx 閘
* `%qiskit_backend_overview` : check information on connective
* `qc.measure_all()` : 在電路**添加新的**傳統位元保存狀態
* `.dim` : 得知電路的維度,假設有2量子位元,就代表維度是2^2 = 4維,另外,**輸入維度會等同於輸出維度**
* `QuantumRegister(7)` : 創建一個7量子位元的儲存器,英文描述會長這樣:quantum register with 7 qubits
* **保真度** : 檢查2者是否相同,也就是說,假設2個相同的閘就算全局變量不一樣保真度還是會相同,保真度等於 1
而看保真度的程式碼是 **`average_..._fidelity`** : ...當中可以填入gate或state,但比較的雙方一定要是一樣的屬性,如果跑出來等於 1 ,就代表2者是相同的,下方是舉例 :
$\Longrightarrow
\begin{cases} X gate = exp(-1j / 2) * X gate \ \ \ (gate 對上gate)\\
\\
[0,1] = [0, 1j] \ \ \ (state 對上 state)
\end{cases}$ ,這2者的保真度都是 1
另外看保真度的函式其實有這3種,通常選項出現3種問說哪些是對的時要選 **`average_..._fidelity`** 以及 **`process_fidelity`** , 但 **`state_fidelity`** 是錯誤的(我也不知為啥)
* `sv.draw('hinton')` 或是 `sv.draw('hinton_plot')` : 用 statevector 畫出 hinton plot
* `qc.qasm()` : print in QASM syntax
* `plot_barriers = False` in QuantumCircuit.draw : 不會顯示出barriers
* `plot_gate_map` 還有 `plot_error_map` : show the gate map of the device
* `qc.draw('...',style = {'backgroundcolor' = '<color>'})` : 畫出電路圖, ... 當中可以填上這3種 $\Longrightarrow$
$\begin{cases}
i. \ \ \ mpl : 一般的電路圖,常看到的那種(內建) \\
ii. \ \ latex : 高質量的圖 \\
iii. \ text : ASCII 的表示圖
\end{cases}$
* `QuantumRegister(int, name = '')` 是叫出量子位元,可以附上名字,假設名字寫 a ,那出來的電路會寫 a0, a1 ,......;
`ClassicalRegister(int, name = '')` 是叫出傳統位元,可以附上名字;
`QuantumCircuit(q,c)` 是創建量子電路,裡面可以放入上述2種位元或是用 int 叫出
* `qc.measure(qubit, cbit)` : 當中填入的 qubit & cbit 可以用 list 取代,像這樣
`qc.measure([0,1,2],[0,2,1])` 是代表 $\begin{cases}
0 \ qubit \Rightarrow 0 \ cbit \\
1 \ qubit \Rightarrow 2 \ cbit \\
2 \ qubit \Rightarrow 1 \ cbit
\end{cases}$
* 如果要在所有 qubits 中放上 barriers 可以用以下方法 :
```
# 先假設創建 3 qubits & 3 cbits
qc = QuantumCircuit(3,3)
# 第一種
qc.barrier([0,1,2])
# 第二種
qc.barrier([0:3])
# 第三種
qc.barrier()
# 第四種
qc.barrier(0,1,2)
```
* 要輸出 matrix1 $\otimes$ matrix2 :
```
matrix1 = DensityMatrix(matrix1)
result = matrix1.tensor(matrix2)
print(result)
```
* 創建一個用 QASM 字串表達的量子電路 :
```
qc = QuantumCircuit.from_qasm_str(qasm_str)
```
* `qc.initialize(state, qubit)` : state 可以放入 list 或 int 之類的,範例如下 $\Longrightarrow$
假設要創建這樣的 **statevector : [0.707+0.j, 0.+0.j, 0.+0.j, 0.707+0.j]** 可以由以下2種方法達成
```
# 第一種
qc = QuantumCircuit(2)
v = [1/sqrt(2), 0, 0, 1/sqrt(2)]
qc.initialize(v,[0,1])
#第二種
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
```
但如果有 2 個 qubit 的話就要這樣寫
```
qc.initialize([1,0,0,0],[0,1])
```
* `plot_state_city(sv, color = ["red","red"])` : 做出 3 維的柱狀圖,柱子顏色是紅色
* `plot_histogram(dict)` : dict 放入字典,並且在後面可以加上 bar_labels=False ,讓圖上不會出現數字,下面是舉例
`plot_histogram({'000' : 450, '111' : 550}, bar_labels = False)`
如果沒有 bar_labels=False 的話在橘色圈圈處會出現 450 以及 550

* `plot_bloch_multivector` : 在球上有 statevector ,有多少 qubit 就會有多少球
`plot_bloch_vector` : 只會出現 1 顆球而已
`plot_bloch_cartesian` : 以笛卡爾座標繪製,在球上標點而已
* `qc.decompose().draw()` : 把電路上的 gate 拆成單量子或是雙量子位元的 gate (英文會這樣描述 : draw the circuit as single and two-qubits gates only),像是假設電路上放了 ccx gate 那就會被拆成很多 H CX T gate 之類的
* `plot_error_map()` : 畫出錯誤率圖,長的像下圖這樣

* `plot_gate_map()` : 畫出 物理qubit 以及 邏輯qubit 之間的關係,幫助了解現在的量子拓樸結構,長的像下圖這樣

* `execute(..., ..., optimization_level)` : in the `execute` function, the parameter -- optimization_level can set how much optimization to perform on the circuit
* `plot_bloch_multivector(vector)` : vector 是要放入經過計算之後的 statevector,下方是舉例
要產生這個圖 :

首先我們知道要用 plot_bloch_multivector
接著可以透過圖得知 qubit 0 沒有經過 gate 的旋轉,但 qubit 1 經過了 H gate 的旋轉
那我們就可以先畫出電路圖 :

再來可以去算 statevector :
$q_0 : \begin{bmatrix} 1 \\ 0 \end{bmatrix}$
$q_1 : \begin{bmatrix} 1 \\ 0 \end{bmatrix} \times \begin{bmatrix} \cfrac{1}{\sqrt{2}} \ \cfrac{1}{\sqrt{2}} \\ \cfrac{1}{\sqrt{2}} \ \cfrac{-1}{\sqrt{2}} \end{bmatrix} = \begin{bmatrix} \cfrac{1}{\sqrt{2}} \\ \cfrac{1}{\sqrt{2}} \end{bmatrix}$
我們知道了 $q_0, q_1$ 各自的 statevector 後接著就可以算綜合的 :
$\begin{bmatrix} 1 \\ 0 \end{bmatrix} \otimes \begin{bmatrix} \cfrac{1}{\sqrt{2}} \\ \cfrac{1}{\sqrt{2}} \end{bmatrix} = \begin{bmatrix} \cfrac{1}{\sqrt{2}} \\ 0 \\ \cfrac{1}{\sqrt{2}} \\ 0 \end{bmatrix}$
所以如果要畫出上述的2顆球的程式碼就會是這樣 :
`plot_bloch_multivector([1/sqrt(2),0, 1/sqrt(2),0])`
* `job.status()` 以及 `job_monitor(job)` : 查看目前的 job 的詳細資料(to get the information of the job when `job = execute(qc,backend)`)
補充 : 沒有這幾個用法
- `job_monitor(backend)` : 不能把 backend 放在裡面
- `provider.status()` : Account 不能得知目前狀態
- `backend.status()` : 雖然可以執行,但不能得知 job 的詳細資料,只能得到 backend 的詳細資料
* `random_unitary(2)` : 輸出 $[[\ ... ,\ ...], \ [\ ...,\ ...]],\ input\ dim = 2, \ output\ dim = 2$
`random_statevector(2)` : 輸出 $[...,\ ...],\dim = 2$
## **重要觀念**
* circuit 也就是電路才有辦法 draw 出來, Register 或閘門之類的不能單純 draw 出來
* 閱讀這種類型的東西時,注意是從高位到低位:|00>+|11> $\Rightarrow$ |00>+|10> 代表是經過CNOT並且控制的部分是 1 號位元而目標是 0 號位元
* `.get_backend()` 非常重要,就是要用哪個simulator來當後端
* 只有 `plot_histogram` 沒有 `plot_bar_chart` 之類的,但可以透過`plot_histogram([c1, c2],...)` 來達成製作 bar 圖的目的
* **text** is the default output method used to draw the circuit
* `execute` 這個函數非常重要, **`execute(qc, sim, backend, basic_gates = [...], coupling_map = [[...],[...],...], optimization_level = n).result().get_memory()`** 這些都是常用會加上去的,請注意除了這些之外其他一些奇怪的不會加,例如甚麼mode, device之類的就不會
* 如果要建立最大糾纏態 (entangle value) ,以 2 qubit 為例 :
```
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
# 注意 cx 的部分不可被改為 cy 或 cz
```
* 基礎的 python 觀念 : [ a : b ] 是代表**輸出 a ~ (b-1)** 的數字不是 a~b
* 判斷電路深度 : 畫出來,下面是範例
```
qc = QuantumCircuit(5)
qc.cx(0,1)
qc.cx(2,3)
qc.cx(3,4)
```

畫出來會長這樣,所以深度是 2
* 不存在以下函數 :
- 關於 bar_chart 的
- `plot_error.gate_map()`
- `plot_VisualizationError()`
- `qc.cexchange()`
- `qc.ct()`
# Bloch Sphere
先釐清一個重要觀念,所謂的rotate X(或是Y、Z之類的),就是圍繞該軸旋轉,像是下圖:

對於 |0> 來說原本是指向 0 的相位(粉色的線),經過 RY gate 的旋轉 $\pi$ 後(藍色的線),會變成圖上箭頭的相位,也就是 1 ;同時最重要的,他的狀態向量會從 1|0> + 0|1> 變成 0|0> + 1|1>,在"|"前面的是出現的機率,後面的是出現的相位會是 0 或是 1
所以同理可以知道這樣是經過RY gate轉了 $3/4 \pi$後的結果(箭頭始終在x-z平面上旋轉所以只有經過RY gate 而已)

接下來介紹箭頭指向不同方向的 statevector 是甚麼 :

然後這是不同方向對應到的 statevector :

除此之外,如果是純vector的話就是一般的 3 維概念 :
像這張圖片就是用這個向量表現出來的 [1/sqrt(2),-1/sqrt(2),0]

接著,我們要判斷有多少 qubits 在 qsphere 上,可以藉由這些方法 :
先藉由數上面共有多少個點,像現在有8個點,也就是 2^3,所以共有 3 qubits

但像這個上面就已經有標向量了就看是幾位數,像 |000>就是 3 qubits

* **補充** :
**逆時針方向是正向**,非常重要!!!
# Gate
**(以下沒特別註明的話都是放在qubit中)**
**$\cfrac{1}{\sqrt{2}} = 0.707$**
* **H gate** :
函數 : `qc.h(target)`
電路圖 : `qc.h(0)`

矩陣 : $\cfrac{1}{\sqrt{2}} \times \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix}$
補充 : H = Ry($\cfrac{\pi}{2}$) * Rx($\pi$), 如果有 2 個 H gate 會互相抵銷回到原始態
另外 H gate 的bloch sphere 長這樣 :

* **T gate** :
函數 : `qc.t(target)`
電路圖 : `qc.t(0)`

矩陣 : $\begin{bmatrix}
1 & 0\\
0 & e^{i \cdot \pi/4}\\
\end{bmatrix}$
補充 : 等同於繞 Z 軸旋轉 $\cfrac{\pi}{4}$,也就是說 T = Rz($\cfrac{\pi}{4}$)
* **S gate** :
函數 : `qc.s(target)`
電路圖 : `qc.s(0)`

矩陣 : $\begin{bmatrix}
1 & 0\\
0 & e^{i \cdot \pi/2}\\
\end{bmatrix}$
補充 : 等同於繞 Z 軸旋轉 $\cfrac{\pi}{2}$,也就是說 $S$ = $T^2$ = $Rz(\cfrac{\pi}{2})$
* **Sdg gate** :
函數 : `qc.sdg(target)`
電路圖 : `qc.sdg(0)`

矩陣 : $\begin{bmatrix}
1 & 0\\
0 & e^{-i \cdot \pi/2}\\
\end{bmatrix}$
補充 : 等同於 S gate 的逆門(共軛),也就是說會跟 S gate 抵銷掉,$S^{\dagger} = Rz(-\cfrac{\pi}{2})$
* **X gate** :
函數 : `qc.x(target)`
電路圖 : qc.x(0)

矩陣 : $\begin{bmatrix}
0 & 1\\
1 & 0\\
\end{bmatrix}$
* **Y gate** :
函數 : `qc.y(target)`
電路圖 : qc.y(0)

矩陣 : $\begin{bmatrix}
0 & -i\\
i & 0\\
\end{bmatrix}$
補充 : 是 X gate 和 Z gate 的組合,也就是說 Y gate 不只交換了 |0> 和 |1> 的狀態,還在結果狀態中添加了 i 的相對相位
* **Z gate** :
函數 : `qc.z(target)`
電路圖 : qc.z(0)

矩陣 : $\begin{bmatrix}
1 & 0\\
0 & -1\\
\end{bmatrix}$
補充 : 對 |0>、|1>沒有影響,但會更改相位,像是$\begin{cases}
Z|0> = \ \ \ |0> \\
Z|1> = -|1>
\end{cases}$
* **CNOT (CX) gate** :
函數 : `qc.cx(control, target)`
電路圖 : `qc.cx(0, 1)`

矩陣 : $\begin{bmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 0 & 1\\
0 & 0 & 1 & 0\\
\end{bmatrix}$
* **CRY gate** :
函數 : `qc.cry(theta, control, target)`
電路圖 : `qc.cry(np.pi, 0,1)`

* **MCT gate** : 一次串聯好幾個控制位元(用 list 串起來)
函數 : `mct([x1, x2, x3, ...], target)`
電路圖 : `qc.mct([0,1,2], 3)`

* **CCNOT (CCX) gate** :
函數 : `qc.ccx(control_q1, control_q2, target)`
電路圖 : `qc.ccx(0,1,2)`

* **CZ gate** :
函數 : `qc.cz(control, target)`
電路圖 : `qc.cz(0,1)`

矩陣 : $\begin{bmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 1 & 0\\
0 & 0 & 0 & -1\\
\end{bmatrix}$
* **補充-1** : 其實不只有CZ可以對 Z 軸旋轉,事實上可以藉由這 3 種 gate 達到:
CZ, CP, CRZ,對應到的 gate 依序長這樣 $\Longrightarrow$

此外 CZ gate 等價於 H, CNOT, H 組合再一起

* **補充-2** : 創建 Bell state
```
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
```
statevector 長這樣
電路圖長這樣 
* **補充-3** : 創建 GHZ state
```
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
```
statevector 長這樣 
電路圖長這樣 
* **補充-4** :
```
from qiskit import QuantumCircuit, Aer
qc = QuantumCircuit(1)
qc.h(0)
qc.x(0)
simulator=Aer.get_backend('unitary_simulator')
result = execute(qc,simulator).result()
unitary = result.get_unitary(qc)
print(unitary)
```
在 unitary_simulator 中執行這段程式碼會得到這個2維矩陣(非1維),而矩陣的值可以通過計算經過的 gate 得到

* **補充-5** : Pauli gates
- X = HZH
- Z = HXH
- Y = |1>, $\pi$/2
HYH = |1>, $3\pi$/2
- XY = XYZ = $| \uparrow >$, $3\pi$/2
- XZ = $| \downarrow >$, $\pi$
- YZ = $| \downarrow >$, $3\pi$/2
- XX = $| \uparrow >$, 0
* **補充-6** : 旋轉門
- 不能回到初始值 :
$\begin{cases}
crz(\pi, 0, 1) \\
cp(\pi, 0, 1)
\end{cases}$
- 可以回到初始值 :
$\begin{cases}
cz(0, 1) \\
cp(\pi, 0, 1)
\end{cases}$
\
$\begin{cases}
crz(\pi, 0, 1) \\
crz(-\pi, 0, 1)
\end{cases}$
\
$\begin{cases}
cz(0, 1) \\
cz(1, 0)
\end{cases}$
* **補充-7** : 暱稱
- Reverse-Flip : H gate
- Control-Flip : CNOT gate
- Phase-Flip : Z gate
- Bit-Flip : X gate
# Qiskit & Simulators
* Qiskit 共有4大組件,分別是 :
1. Terra : 提供基本電路元件
2. Aer : 提供用於傳統電腦的量子模擬
3. Aqua : 提供關於量子演算法
4. Ignis : 提供關於減少雜訊跟誤差的物件
* 然後有這幾個重要的 simulator :
1. aer_simulator : 提供統計結果
輸出結果會類似這樣 : {'11': 504, '00': 520}
2. unitary_simulator : 輸出是用單位矩陣表示
3. aer_simulator_density_matrix : 輸出是用密度矩陣表示
4. statevactor_simulator : 提供完整的狀態向量
輸出結果會類似這樣 : Statevector([0.70710678+0.j, 0.+0.j, 0.+0.j,0.70710678+0.j],dims=(2, 2))
5. ibmq_manila : 可以跑出字典以及生成柱狀圖
* **在 BasicAer 中可以用的模擬器有這3個**:
1. **qasm_simulator** : 模擬電路的運行&生成結果
2. **unitary_simulator** : 計算電路的結果,用矩陣表示
3. **statevector_simulator** : 計算電路的結果,用向量表示
補充 : 沒有basic_qasm_simulator, quantum....之類的
* 在ibmq quito當中畫出讀取誤差 :
```
provider = IBMQ.load_account()
backend = provider.get_backend('ibmq_quito')
plot_error_map(backend)
```
* assign qasm_simulator :
1. `BasicAer.get_backend('qasm_simulator')`
2. `Aer.get_backend('qasm_simulator')`
* obtain the unitary of a circuit : `unitary_simulator`
* 在 OpenQASM 當中創建 1 qubit & 1 cbit 的疊加態 :
```
qreg q[1] # 建 1 qubit
creg c[1] # 建 1 cbit
h q[0] # 在 qubit 上放 H gate
```
* 區分 Aer 跟 BasicAer 的用法:
- Aer :
```
Aer.get_backend("unitary_simulator")
result = execute(qc, simulator).result()
unitary = result.get_unitary(qc)
```
- BasicAer :
```
BasicAer.get_backend("unitary_simulator")
result = execute(qc, simulator).result()
unitary = result.get_unitary(qc)
```
結論 : 似乎沒什麼差別,都要先經過 get_backend execute 最後是 get_unitary
* `BasicAer.backends()` : 列出所有可用的 backends(會用 list 表示)
# Statevalue & Statevector
* `Statevector.from_int(3,2**3)` : 第一個 3 代表改變第 3 個(從 0 開始算,左到右),第二部分 2 ** 3 代表共有 2 ** 3=8 個qubits
* 如果 qubits 是特徵態 (eigenstate) : 那該電路的statevector會是最少並且全部包含,像是 $\Longrightarrow$ 在 3 qubits 中會出現 000 --- 111、 001 --- 110、 011 --- 100之類的
* 注意 qiskit 當中的 qubit 是依照(大到小)顯示的,也就是
{'$\underbrace{0}_{這是第2號位元}$ $0$ $\underbrace{1}_{這是第0號位元}$ $:$ $1000$ '}
這樣是經過了qc.x(0)所產生的結果
* eigenstate of $\begin{cases}
X \ axis \Rightarrow |+> ,\ |-> \\
Y \ axis \Rightarrow |1> ,\ |-1> \\
Z \ axis \Rightarrow |\Psi_1> , \ |\Psi_2> (\Psi是非0複數)
\end{cases}$
* 經過 CX gate :
eg.
```
qc.h(0) # q_0 : 1/sqrt(2) * (|0>+|1>), q_1 : |0>
qc.cx(0,1)
# 先把q_0 q_1 綜合起來(用otimes) : 1/sqrt(2) * (|00>+|01>)
# 接者套用 cx gate 的概念 當q_0 是 1 的時後翻轉 q_1
# 最後得到 1/sqrt(2) * (|00>+|11>)
* 閱讀 `plot_state_city` : (目前其實也還沒很懂)
先看會出哪幾種 state (|00>, |11> 之類的)
而至於會出現甚麼 state 需要自己計算
eg. 創建 Bell State :
```
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
```
從程式碼中我們可以計算出 state = $\cfrac{1}{\sqrt{2}} \times (|00>+|11>)$
所以在 `plot_state_city` 中我們只會看到 00 跟 11 有柱子而已

# 參考資料
1. 題目練習--1 :
https://medium.com/@shraddhaaangiras0911/full-length-qiskit-certification-test-9a4f0b12d5d5
2. 題目練習--2 :
https://slides.com/javafxpert/prep-qiskit-dev-cert-exam#/18/0/0
3. chatgpt教我怎麼作圖還有印出向量,程式碼如下 :
```
# 做出ry gate並且輸出statevector以及blochsphere
from qiskit import QuantumCircuit, Aer, transpile, assemble
from qiskit.visualization import plot_bloch_multivector
import numpy as np
theta = np.pi
# 創建量子電路
qc = QuantumCircuit(1)
a = qc.ry(theta, 0)
# 使用模擬器來模擬量子電路並取得輸出狀態向量
simulator = Aer.get_backend('statevector_simulator')
qc_transpiled = transpile(qc, simulator)
result = simulator.run(qc_transpiled).result()
statevector_result = result.get_statevector()
print(statevector_result)
# 繪製Bloch球
plot_bloch_multivector(statevector_result)
```
4. 教我基礎知識的網站(裡面還有很多可以探索) :
https://www.sharetechnote.com/html/QC/QuantumComputing_BlochSphere.html
5. 其他人寫的復習手冊 :
https://github.com/bartubisgin/qiskit-certified-exam-workbook/blob/main/Workbook-for-Qiskit-Developer-Certification.ipynb