ket/ir/
instructions.rs

1// SPDX-FileCopyrightText: 2024 Evandro Chagas Ribeiro da Rosa <evandro@quantuloop.com>
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use super::{
6    gate::{Matrix, QuantumGate},
7    hamiltonian::Hamiltonian,
8    qubit::{LogicalQubit, PhysicalQubit, Qubit},
9};
10use crate::mapping::allocation::Mapping;
11use serde::{Deserialize, Serialize};
12use std::hash::Hash;
13
14#[derive(Debug, Clone, Default, Serialize, Deserialize)]
15pub enum Instruction<Q> {
16    Gate {
17        gate: QuantumGate,
18        target: Q,
19        control: Vec<Q>,
20    },
21    #[default]
22    Identity,
23    Measure {
24        qubits: Vec<Q>,
25        index: usize,
26    },
27    Sample {
28        qubits: Vec<Q>,
29        index: usize,
30        shots: usize,
31    },
32    Dump {
33        qubits: Vec<Q>,
34        index: usize,
35    },
36    ExpValue {
37        hamiltonian: Hamiltonian<Q>,
38        index: usize,
39    },
40    U2Gates {
41        gates: Vec<QuantumGate>,
42        qubit: Q,
43    },
44}
45
46impl<Q> Instruction<Q>
47where
48    Q: Qubit + Eq + Hash + Clone + Copy,
49{
50    pub(crate) fn qubits(&self) -> impl Iterator<Item = &Q> {
51        use genawaiter::{rc::gen, yield_};
52        gen!({
53            match self {
54                Instruction::Gate {
55                    target, control, ..
56                } => {
57                    yield_!(target);
58                    for qubit in control {
59                        yield_!(qubit);
60                    }
61                }
62                Instruction::Measure { qubits, .. }
63                | Instruction::Sample { qubits, .. }
64                | Instruction::Dump { qubits, .. } => {
65                    for qubit in qubits {
66                        yield_!(qubit);
67                    }
68                }
69                Instruction::ExpValue { hamiltonian, .. } => {
70                    for qubit in hamiltonian.qubits() {
71                        yield_!(qubit);
72                    }
73                }
74                _ => {}
75            }
76        })
77        .into_iter()
78    }
79
80    pub(crate) fn is_ctrl_gate(&self) -> bool {
81        match self {
82            Instruction::Gate { control, .. } => !control.is_empty(),
83            _ => false,
84        }
85    }
86    pub(crate) fn affect_one_qubit(&self) -> bool {
87        match self {
88            Instruction::Gate { control, .. } => control.is_empty(),
89            Instruction::Measure { qubits, .. }
90            | Instruction::Sample { qubits, .. }
91            | Instruction::Dump { qubits, .. } => qubits.len() == 1,
92            Instruction::Identity => true,
93            Instruction::ExpValue { hamiltonian, .. } => {
94                let mut qubits = hamiltonian.qubits();
95                qubits.next().is_some() && qubits.next().is_none()
96            }
97            Instruction::U2Gates { .. } => panic!("this instructions is restrict"),
98        }
99    }
100
101    pub(crate) fn one_qubit_gate(&self) -> bool {
102        match self {
103            Instruction::Gate { control, .. } => control.is_empty(),
104            Instruction::Identity => true,
105            _ => false,
106        }
107    }
108
109    pub(crate) fn matrix(&self) -> Matrix {
110        match self {
111            Instruction::Gate { gate, .. } => gate.matrix(),
112            Instruction::Identity => QuantumGate::Phase(0.0.into()).matrix(),
113            _ => panic!("matrix available only for gate instruction"),
114        }
115    }
116
117    pub(crate) fn to_usize_qubit(&self) -> Instruction<usize> {
118        match self {
119            Instruction::Gate {
120                gate,
121                target,
122                control,
123            } => Instruction::Gate {
124                gate: *gate,
125                target: target.index(),
126                control: control.iter().map(|c| c.index()).collect(),
127            },
128            Instruction::Identity => Instruction::Identity,
129            Instruction::Measure { qubits, index } => Instruction::Measure {
130                qubits: qubits.iter().map(|q| q.index()).collect(),
131                index: *index,
132            },
133            Instruction::Sample {
134                qubits,
135                index,
136                shots,
137            } => Instruction::Sample {
138                qubits: qubits.iter().map(|q| q.index()).collect(),
139                index: *index,
140                shots: *shots,
141            },
142            Instruction::Dump { qubits, index } => Instruction::Dump {
143                qubits: qubits.iter().map(|q| q.index()).collect(),
144                index: *index,
145            },
146            Instruction::ExpValue { hamiltonian, index } => Instruction::ExpValue {
147                hamiltonian: hamiltonian.to_usize_qubit(),
148                index: *index,
149            },
150            Instruction::U2Gates { .. } => {
151                panic!("U2Gates instruction should not be present at this point.")
152            }
153        }
154    }
155}
156
157impl Instruction<LogicalQubit> {
158    pub(crate) fn map_qubits(&self, mapping: &Mapping) -> Instruction<PhysicalQubit> {
159        match self {
160            Instruction::Gate {
161                gate,
162                target,
163                control,
164            } => Instruction::Gate {
165                gate: *gate,
166                target: *mapping.get_by_left(target).unwrap(),
167                control: control
168                    .iter()
169                    .map(|qubit| *mapping.get_by_left(qubit).unwrap())
170                    .collect(),
171            },
172            Instruction::Measure { qubits, index } => Instruction::Measure {
173                qubits: qubits
174                    .iter()
175                    .map(|qubit| *mapping.get_by_left(qubit).unwrap())
176                    .collect(),
177                index: *index,
178            },
179            Instruction::Sample {
180                qubits,
181                index,
182                shots,
183            } => Instruction::Sample {
184                qubits: qubits
185                    .iter()
186                    .map(|qubit| *mapping.get_by_left(qubit).unwrap())
187                    .collect(),
188                index: *index,
189                shots: *shots,
190            },
191            Instruction::Dump { qubits, index } => Instruction::Dump {
192                qubits: qubits
193                    .iter()
194                    .map(|qubit| *mapping.get_by_left(qubit).unwrap())
195                    .collect(),
196                index: *index,
197            },
198            Instruction::ExpValue { hamiltonian, index } => Instruction::ExpValue {
199                hamiltonian: hamiltonian.map_qubits(mapping),
200                index: *index,
201            },
202            _ => Default::default(),
203        }
204    }
205
206    pub(crate) fn replace_qubit(&mut self, old: LogicalQubit, new: LogicalQubit) {
207        match self {
208            Instruction::Gate {
209                target, control, ..
210            } => {
211                if *target == old {
212                    *target = new;
213                }
214                for qubit in control {
215                    if *qubit == old {
216                        *qubit = new;
217                        break;
218                    }
219                }
220            }
221            Instruction::Identity => {}
222            _ => {
223                panic!("Qubit replace should only happens in gate instruction")
224            }
225        }
226    }
227}