Rust로 양자컴퓨터 시뮬레이터를 만드는 Part2

Rust로 양자컴퓨터의 시뮬레이터를 만들다의 계속.

2 퀀텀 비트 그룹(CNOT 게이트)


이어서 2량자비트의 CNOT 문을 설치해 보자.
회로도와 울타리의 행렬을 수동으로 계산하다.

※ 초기 상태는\ket{00}이므로 신경 쓰지 마십시오.

CNOT


CNOT의 브래킷은 다음과 같습니다.
\ket{0}\bra{0}\otimes I +\ket{1}\bra{1}\otimes X\\
1위가\bra{0}이면\ket{0}이 되고 2위에 대해서는 아무것도 하지 않습니다.(I 도어 적용)
첫 번째가\bra{1}이면\ket{1}이 두 번째 에 X문을 적용합니다.
행렬로 계산하면 다음과 같다.
\ket{0}\bra{0}\otimes I +\ket{1}\bra{1}\otimes X\\
=\begin{pmatrix}
1\\
0\\
\end{pmatrix}\begin{pmatrix}
1 & 0
\end{pmatrix}\otimes I
+\begin{pmatrix}
1\\
0\\
\end{pmatrix}\begin{pmatrix}
1 & 0
\end{pmatrix}\otimes X\\
=\begin{pmatrix}
1 & 0\\
0 & 0\\
\end{pmatrix}\otimes I
+\begin{pmatrix}
0 & 0\\
0 & 1\\
\end{pmatrix}\otimes X\\
=\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
\end{pmatrix}
+\begin{pmatrix}
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
0 & 0 & 0 & 1\\
0 & 0 & 1 & 0\\
\end{pmatrix}\\
=\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 0 & 1\\
0 & 0 & 1 & 0\\
\end{pmatrix}

3량자비트의 회로에 CNOT 적용


이어 양자비트 3개의 회로에 대해 첫 번째와 두 번째 응용 CNOT를 시험해 본다.

※ X문 설치가 까다롭기 때문에 초기 상태를\ket{001} (으)로 설정합니다.

스탠드 마크


(I\otimes I\otimes\ket{0}\bra{0} + I\otimes X\otimes\ket{1}\bra{1})\ket{001}\\
양자 상태를 적용하려면 오른쪽부터 사용하기 때문에 이런 브래킷 표시가 된다.
※ 이 근처는 귀찮아요. 자신이 잘못 알았나 봐요.

행렬 태그


(I\otimes I\otimes\ket{0}\bra{0} + I\otimes X\otimes\ket{1}\bra{1})\ket{001}\\
=\begin{pmatrix}
\begin{pmatrix}
1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
\end{pmatrix}
+\begin{pmatrix}
0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\
\end{pmatrix}
\end{pmatrix}\ket{001}\\
=\begin{pmatrix}
1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\
0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\
0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\
0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\
0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\
\end{pmatrix}\ket{001}\\
=\begin{pmatrix}
1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\
0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\
0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\
0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\
0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\
\end{pmatrix}\begin{pmatrix}
0\\
1\\
0\\
0\\
0\\
0\\
0\\
0\\
\end{pmatrix}\\
=\begin{pmatrix}
0\\
0\\
0\\
1\\
0\\
0\\
0\\
0\\
\end{pmatrix}

프로그램 실행


use qustV2::circuit::QuantumCircuit;
use qustV2::gates::double::DoubleGateApplicator;
use qustV2::gates::single::SingleGateApplicator;

fn main() {
    let n = 3;
    let mut circuit = QuantumCircuit::new(n);
    circuit.X(0);
    circuit.CNOT(0, 1);
    circuit.update_quantum_state();
    println!("{}", circuit);
}
프로그램이 실행될 때 초기 상태는\ket{000}이므로 1위에 먼저 X문을 적용하고 ket{001}을 제작한 후 CNOT를 적용한다.
* Qubit Count  : 3
* Dimension    : 8
* State vector :
000> 0+0i
001> 0+0i
010> 0+0i
011> 1+0i
100> 0+0i
101> 0+0i
110> 0+0i
111> 0+0i
는 수동 계산과 일치한다.

2 양자위 울타리 상황에서의 상태 벡터 인덱스


2 양자 비트문의 경우 상태 벡터는 다음과 같다.
idx
bi0
bi1
bi2
bi3
0-1
0
2
1
3
4
6
5
7
1-2
0
4
2
6
1
5
3
7
  • idx: 양자 비트레이트의 시작 인덱스(0-1의 경우 첫 번째 울타리와 두 번째 울타리가 배치됨)
  • bi0:0을 토대로 얻은 상태 벡터의 인덱스
  • bi1:1을 토대로 얻은 상태 벡터의 인덱스
  • bi2:2를 토대로 얻은 상태 벡터의 인덱스
  • bi3:3을 토대로 얻은 상태 벡터의 인덱스
  • 2 양자위의 경우 각각 상태 벡터에서 4개를 얻어 울타리를 적용한다.

    소스 코드


    간이 된 상태의 벡터에서 얻은 색인은 다음과 같다.
    pub fn indices_vec(
        index: usize,
        qubits_tgt: &Vec<usize>,
        masks: &[usize],
    ) -> Vec<usize> {
        let mut qubits = qubits_tgt.to_owned();
        qubits.sort();
        let mut res = Vec::with_capacity(qubits.len());
        let mask = masks[0];
        let mask_low = masks[1];
        let mask_high = masks[2];
        let basis_0 = (index & mask_low) + ((index & mask_high) << qubits.len());
        let target_mask1 = 1usize << qubits[1];
        let target_mask2 = 1usize << qubits[0];
        let basis_1 = basis_0 + target_mask1;
        let basis_2 = basis_0 + target_mask2;
        let basis_3 = basis_1 + target_mask2;
        res.push(basis_0);
        res.push(basis_1);
        res.push(basis_2);
        res.push(basis_3);
    
        res
    }
    
    mask의 함수mask_vec()를 추출하여 1 양자 비트문과 공유한다.
    기본let basis_0 = (index & mask_low) + ((index & mask_high) << qubits.len())도 1양자 비트문과 같다.
    그리고 target_mask1target_mask2로 폭을 잡습니다.
    let target_mask1 = 1usize << qubits[1];
    let target_mask2 = 1usize << qubits[0];
    let basis_1 = basis_0 + target_mask1;
    let basis_2 = basis_0 + target_mask2;
    let basis_3 = basis_1 + target_mask2;
    

    후불 제어 비트


    CNOT 게이트는 컨트롤 비트와 엑스 게이트의 조합을 통해 제작할 수 있다.
    아래와 같이 X문에 제어 위치를 설정할 수 있다.
    use qustV2::circuit::QuantumCircuit;
    use qustV2::gates::gate::GateOp;
    use qustV2::gates::single::{SingleGateApplicator, X};
    
    fn main() {
        let n = 3;
        let mut circuit = QuantumCircuit::new(n);
        // |001>にする
        circuit.X(0);
    
        // Xゲートに制御ビットを付与する
        let mut x_gate = X(1);
        x_gate.add_control_qubit(0);
        circuit.add_gate(x_gate);
        
        circuit.update_quantum_state();
        println!("{}", circuit);
    }
    
    실행 후 출력은 CNOT와 같습니다.
    * Qubit Count  : 3
    * Dimension    : 8
    * State vector :
    000> 0+0i
    001> 0+0i
    010> 0+0i
    011> 1+0i
    100> 0+0i
    101> 0+0i
    110> 0+0i
    111> 0+0i
    

    제어 위치에 대응하는 indices-vec()


    다음은 컨트롤 비트에 대응하는indices_vec().
    pub fn indices_vec(
        index: usize,
        qubits_ctl: &Vec<usize>,
        qubits_tgt: &Vec<usize>,
        masks: &[usize],
    ) -> Vec<usize> {
        let mut qubits = qubits_tgt.to_owned();
        qubits.sort();
        let mut res = Vec::with_capacity(qubits.len());
        let mask = masks[0];
        let mask_low = masks[1];
        let mask_high = masks[2];
        if qubits.len() == 1 {
            if qubits_ctl.len() > 0 {
                // TODO: 複数制御ビット対応
                let control_mask = 1usize << qubits_ctl[0];
                let qsize = qubits_ctl.len() + qubits_tgt.len();
                let basis_0 = (index & mask_low) + ((index & mask_high) << qsize) + control_mask;
                let target_mask = 1usize << qubits_tgt[0];
                let basis_1 = basis_0 + target_mask;
                res.push(basis_0);
                res.push(basis_1);
            } else {
                let basis_0 = (index & mask_low) + ((index & mask_high) << qubits.len());
                let basis_1 = basis_0 + mask;
                res.push(basis_0);
                res.push(basis_1);
            }
        } else if qubits.len() == 2 {
            let basis_0 = (index & mask_low) + ((index & mask_high) << qubits.len());
            let target_mask1 = 1usize << qubits[1];
            let target_mask2 = 1usize << qubits[0];
            let basis_1 = basis_0 + target_mask1;
            let basis_2 = basis_0 + target_mask2;
            let basis_3 = basis_1 + target_mask2;
            res.push(basis_0);
            res.push(basis_1);
            res.push(basis_2);
            res.push(basis_3);
        } else {
            // TODO
            unimplemented!();
        }
    
        res
    }
    
    여러 제어 비트가 지원되지 않습니다.
    그리고 목표 비트는 1비트만 대응한다.
    다음은 해당되는 부분이다.
    // TODO: 複数制御ビット対応
    let control_mask = 1usize << qubits_ctl[0];
    let qsize = qubits_ctl.len() + qubits_tgt.len();
    let basis_0 = (index & mask_low) + ((index & mask_high) << qsize) + control_mask;
    let target_mask = 1usize << qubits_tgt[0];
    let basis_1 = basis_0 + target_mask;
    res.push(basis_0);
    res.push(basis_1);
    
    의 기초는 let basis_0 = (index & mask_low) + ((index & mask_high) << qsize) + control_mask이다.qsize는 목표 비트와 제어 비트의 사이즈입니다.control_mask1usize << qubits_ctl[0]가 계산한 값이다.
    제어 위치의 인덱스를 왼쪽으로 이동합니다.

    문틈


    현재, 이후에 부여된 제어 위치는 인접 위치에만 한정됩니다.
    즉, X문이 2위에 적용될 때, 제어 위치는 반드시 1위나 3위에 배치해야 한다.
    간격을 두고 배치하려면 마스크를 쓰고 넓게 해야 한다.
    예를 들면,qulacsmid_mask가 바로 그거예요.
    이렇게 문에 틈이 있어도 상관없이 상태 벡터의 색인을 얻을 수 있다면 회로의 최적화를 통해 작은 회로를 하나로 통합하는 것이 편리하다.
    기본적으로 작은 회로를 먼저 합치면 전체적인 계산량이 떨어질 것이다.
    앞으로의 전개로서 나는 이러한 최적화를 진행하고 싶다.
    최근 양산 네트워크가 유행하는 것 같아서 이런 생각으로 최적화를 원한다.

    향후의 발전


    큰 사이즈의 양적 비트에 대응하기 위해 분산화와 병행화를 희망합니다.
    양자문의 크기와 시작 색인에 따라 상태 벡터를 결정하는 색인 추출법.
    따라서 여러 노드에 분산된 양자 시뮬레이터를 고려하는 상황에서 노드 간의 통신을 최대한 줄이기 위해
    회로 최적화를 고려할 수 있다.
    슈퍼컴퓨터'부악'의 기술을 이용하여 36개의 양자위를 발산하는 세계 가장 빠른 양자 시뮬레이터: 후지통에서 후지통은 qulacs와 OpenMPI를 상대적으로 대응하고 분산 병행화했다.
    내가 읽은 논문에 따르면 [2203.16044] mpiQulacs: A Distributed Quantum Computer Simulator for A64FX-based Cluster Systems SWWAP 문을 열고 회로를 최적화하여 노드 간의 통신을 줄이는 것 같다.
    나도 장량 네트워크를 이용하여 각양각색의 최적화를 진행하고 싶다.

    좋은 웹페이지 즐겨찾기