ket/process/
ctrl.rs

1// SPDX-FileCopyrightText: 2025 Evandro Chagas Ribeiro da Rosa <evandro@quantuloop.com>
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::error::{KetError, Result};
6use crate::ir::qubit::LogicalQubit;
7use crate::process::Process;
8
9#[derive(Debug)]
10pub(super) struct CtrlEngine {
11    /// Control qubits stack.
12    pub stack: Vec<Vec<Vec<LogicalQubit>>>,
13    /// List of control qubits.
14    list: Vec<LogicalQubit>,
15    /// If the list of control qubits are up to date.
16    is_list_valid: bool,
17}
18
19impl Default for CtrlEngine {
20    fn default() -> Self {
21        Self {
22            stack: vec![Vec::new()],
23            list: Default::default(),
24            is_list_valid: Default::default(),
25        }
26    }
27}
28
29impl CtrlEngine {
30    /// Get control qubit list
31    pub fn get_list(&mut self) -> &[LogicalQubit] {
32        if !self.is_list_valid {
33            self.list = self
34                .stack
35                .last()
36                .unwrap()
37                .clone()
38                .into_iter()
39                .flatten()
40                .collect();
41            self.is_list_valid = true;
42        }
43        &self.list
44    }
45
46    /// Add qubits to control qubit list
47    pub fn push(&mut self, qubits: &[LogicalQubit]) -> Result<()> {
48        if qubits.iter().any(|qubit| self.get_list().contains(qubit)) {
49            return Err(KetError::ControlTwice);
50        }
51
52        self.stack.last_mut().unwrap().push(qubits.to_owned());
53        self.is_list_valid = false;
54
55        Ok(())
56    }
57
58    /// Remover qubits from the control qubit list
59    pub fn pop(&mut self) -> Result<()> {
60        self.is_list_valid = false;
61
62        if self.stack.last_mut().unwrap().pop().is_none() {
63            Err(KetError::ControlStackEmpty)
64        } else {
65            Ok(())
66        }
67    }
68
69    /// Start a new control qubit list
70    pub fn begin(&mut self) -> Result<()> {
71        self.stack.push(vec![]);
72        self.is_list_valid = false;
73        Ok(())
74    }
75
76    /// End the control qubit list
77    pub fn end(&mut self) -> Result<()> {
78        match self.stack.pop() {
79            Some(stack) => {
80                if !stack.is_empty() {
81                    Err(KetError::ControlStackNotEmpty)
82                } else {
83                    self.is_list_valid = false;
84                    if self.stack.is_empty() {
85                        Err(KetError::ControlStackRemovePrimary)
86                    } else {
87                        Ok(())
88                    }
89                }
90            }
91            None => Err(KetError::ControlStackRemovePrimary),
92        }
93    }
94}
95
96impl Process {
97    pub fn ctrl_push(&mut self, qubits: &[LogicalQubit]) -> Result<()> {
98        self.adj_ctrl_checks(Some(qubits))?;
99        self.ctrl.push(qubits)
100    }
101
102    pub fn ctrl_pop(&mut self) -> Result<()> {
103        self.ctrl.pop()
104    }
105
106    pub fn adj_begin(&mut self) -> Result<()> {
107        self.adj_ctrl_checks(None)?;
108        self.adj_stack.push(vec![]);
109        Ok(())
110    }
111
112    pub fn adj_end(&mut self) -> Result<()> {
113        if let Some(mut gates) = self.adj_stack.pop() {
114            while let Some(gate) = gates.pop() {
115                self.push_gate(gate.inverse());
116            }
117            Ok(())
118        } else {
119            Err(KetError::InverseScopeEmpty)
120        }
121    }
122
123    pub fn ctrl_begin(&mut self) -> Result<()> {
124        self.adj_ctrl_checks(None)?;
125        self.ctrl.begin()
126    }
127
128    pub fn ctrl_end(&mut self) -> Result<()> {
129        self.adj_ctrl_checks(None)?;
130        self.ctrl.end()
131    }
132}